2017-01-16 20:59:50 +01:00
/ * Copyright ( c ) Citrix Systems , Inc .
2013-06-24 13:41:48 +02:00
* 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 ;
2013-12-05 13:46:39 +01:00
using System.ComponentModel ;
2013-06-24 13:41:48 +02:00
using System.Linq ;
using XenAdmin.Actions ;
using XenAPI ;
using XenAdmin.Alerts ;
using XenAdmin.Network ;
2016-02-02 20:12:38 +01:00
using System.Diagnostics ;
2016-11-16 13:26:26 +01:00
using System.Windows.Forms ;
using XenAdmin.Dialogs ;
2017-04-26 13:45:49 +02:00
using System.Text ;
2017-11-17 02:04:45 +01:00
using XenCenterLib ;
2013-06-24 13:41:48 +02:00
namespace XenAdmin.Core
{
public class Updates
{
private static readonly log4net . ILog log = log4net . LogManager . GetLogger ( System . Reflection . MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
2013-11-21 15:27:34 +01:00
public static event Action < bool , string > CheckForUpdatesCompleted ;
2013-12-05 13:46:39 +01:00
public static event Action CheckForUpdatesStarted ;
2016-08-08 15:37:47 +02:00
public static event Action RestoreDismissedUpdatesStarted ;
2013-11-21 15:27:34 +01:00
2014-01-08 14:17:02 +01:00
private static readonly object downloadedUpdatesLock = new object ( ) ;
2015-08-04 18:28:29 +02:00
private static List < XenServerVersion > XenServerVersionsForAutoCheck = new List < XenServerVersion > ( ) ;
2013-12-05 13:46:39 +01:00
private static List < XenServerPatch > XenServerPatches = new List < XenServerPatch > ( ) ;
2014-01-08 14:17:02 +01:00
private static List < XenCenterVersion > XenCenterVersions = new List < XenCenterVersion > ( ) ;
2016-06-29 13:49:53 +02:00
public static List < XenServerVersion > XenServerVersions = new List < XenServerVersion > ( ) ;
2013-12-05 13:46:39 +01:00
private static readonly object updateAlertsLock = new object ( ) ;
private static readonly ChangeableList < Alert > updateAlerts = new ChangeableList < Alert > ( ) ;
public static IEnumerable < Alert > UpdateAlerts
2013-11-21 15:27:34 +01:00
{
get { return updateAlerts ; }
}
2013-12-05 13:46:39 +01:00
public static int UpdateAlertsCount
{
get { return updateAlerts . Count ; }
}
private static void AddUpate ( Alert update )
{
try
{
lock ( updateAlertsLock )
2015-07-29 14:43:47 +02:00
{
2015-08-03 16:12:42 +02:00
if ( ! updateAlerts . Contains ( update ) )
{
updateAlerts . Add ( update ) ;
}
2015-07-29 14:43:47 +02:00
}
2013-12-05 13:46:39 +01:00
}
catch ( Exception e )
{
log . Error ( "Failed to add update" , e ) ;
}
}
2015-07-23 12:48:14 +02:00
public static void RemoveUpdate ( Alert update )
2013-12-05 13:46:39 +01:00
{
try
{
lock ( updateAlertsLock )
2015-08-04 17:27:12 +02:00
{
if ( updateAlerts . Contains ( update ) )
{
updateAlerts . Remove ( update ) ;
}
}
2013-12-05 13:46:39 +01:00
}
catch ( Exception e )
{
log . Error ( "Failed to remove update" , e ) ;
}
}
2015-07-29 14:43:47 +02:00
/// <summary>
/// Dismisses the updates in the given list i.e. they are added in the
/// other_config list of each pool and removed from the Updates.UpdateAlerts list.
/// </summary>
/// <param name="toBeDismissed"></param>
public static void DismissUpdates ( List < Alert > toBeDismissed )
{
2015-11-05 16:49:38 +01:00
if ( toBeDismissed . Count = = 0 )
return ;
2015-07-29 14:43:47 +02:00
foreach ( IXenConnection connection in ConnectionsManager . XenConnectionsCopy )
{
2015-11-05 16:49:38 +01:00
if ( ! Alert . AllowedToDismiss ( connection ) )
continue ;
2015-07-29 14:43:47 +02:00
XenAPI . Pool pool = Helpers . GetPoolOfOne ( connection ) ;
if ( pool = = null )
continue ;
Dictionary < string , string > other_config = pool . other_config ;
foreach ( Alert alert in toBeDismissed )
{
if ( alert is XenServerPatchAlert )
{
if ( other_config . ContainsKey ( IgnorePatchAction . IgnorePatchKey ) )
{
List < string > current = new List < string > ( other_config [ IgnorePatchAction . IgnorePatchKey ] . Split ( ',' ) ) ;
2016-02-08 11:14:05 +01:00
if ( current . Contains ( ( ( XenServerPatchAlert ) alert ) . Patch . Uuid , StringComparer . OrdinalIgnoreCase ) )
2015-07-29 14:43:47 +02:00
continue ;
current . Add ( ( ( XenServerPatchAlert ) alert ) . Patch . Uuid ) ;
other_config [ IgnorePatchAction . IgnorePatchKey ] = string . Join ( "," , current . ToArray ( ) ) ;
}
else
{
other_config . Add ( IgnorePatchAction . IgnorePatchKey , ( ( XenServerPatchAlert ) alert ) . Patch . Uuid ) ;
}
}
if ( alert is XenServerVersionAlert )
{
if ( other_config . ContainsKey ( IgnoreServerAction . LAST_SEEN_SERVER_VERSION_KEY ) )
{
List < string > current = new List < string > ( other_config [ IgnoreServerAction . LAST_SEEN_SERVER_VERSION_KEY ] . Split ( ',' ) ) ;
if ( current . Contains ( ( ( XenServerVersionAlert ) alert ) . Version . VersionAndOEM ) )
continue ;
current . Add ( ( ( XenServerVersionAlert ) alert ) . Version . VersionAndOEM ) ;
other_config [ IgnoreServerAction . LAST_SEEN_SERVER_VERSION_KEY ] = string . Join ( "," , current . ToArray ( ) ) ;
}
else
{
other_config . Add ( IgnoreServerAction . LAST_SEEN_SERVER_VERSION_KEY , ( ( XenServerVersionAlert ) alert ) . Version . VersionAndOEM ) ;
}
}
Updates . RemoveUpdate ( alert ) ;
}
XenAPI . Pool . set_other_config ( connection . Session , pool . opaque_ref , other_config ) ;
}
}
2013-12-05 13:46:39 +01:00
private static Alert FindUpdate ( Alert alert )
{
lock ( updateAlertsLock )
return FindUpdate ( a = > a . Equals ( alert ) ) ;
}
private static Alert FindUpdate ( Predicate < Alert > predicate )
{
lock ( updateAlertsLock )
return updateAlerts . Find ( predicate ) ;
}
2013-11-21 15:27:34 +01:00
2014-01-08 14:17:02 +01:00
/// <summary>
/// If AutomaticCheck is enabled it checks for updates regardless the
2017-02-24 13:03:03 +01:00
/// value of the parameter force. If AutomaticCheck is disabled it
/// checks for all update types if force is true; forceRefresh causes
/// the check for update action to run and refresh the Updates page
2014-01-08 14:17:02 +01:00
/// </summary>
2017-02-24 13:03:03 +01:00
public static void CheckForUpdates ( bool force , bool forceRefresh = false )
2014-01-08 14:17:02 +01:00
{
if ( Helpers . CommonCriteriaCertificationRelease )
return ;
if ( Properties . Settings . Default . AllowXenCenterUpdates | |
Properties . Settings . Default . AllowXenServerUpdates | |
2017-02-24 13:03:03 +01:00
Properties . Settings . Default . AllowPatchesUpdates | | force | | forceRefresh )
2014-01-08 14:17:02 +01:00
{
2017-04-26 13:45:49 +02:00
var action = CreateDownloadUpdatesXmlAction (
2014-01-08 14:17:02 +01:00
Properties . Settings . Default . AllowXenCenterUpdates | | force ,
Properties . Settings . Default . AllowXenServerUpdates | | force ,
2016-02-04 11:39:14 +01:00
Properties . Settings . Default . AllowPatchesUpdates | | force ,
2016-06-07 17:02:43 +02:00
Updates . CheckForUpdatesUrl ) ;
2016-08-08 15:37:47 +02:00
action . Completed + = actionCompleted ;
2014-01-08 14:17:02 +01:00
if ( CheckForUpdatesStarted ! = null )
CheckForUpdatesStarted ( ) ;
action . RunAsync ( ) ;
}
}
2016-06-07 17:02:43 +02:00
2017-04-26 13:45:49 +02:00
private static DownloadUpdatesXmlAction CreateDownloadUpdatesXmlAction ( bool checkForXenCenter , bool checkForServerVersion , bool checkForPatches , string checkForUpdatesUrl = null )
{
2017-07-03 18:54:57 +02:00
string userAgent = string . Format ( "{0}/{1}.{2} ({3}-bit)" , Branding . BRAND_CONSOLE , Branding . XENCENTER_VERSION , Program . Version . Revision . ToString ( ) , IntPtr . Size * 8 ) ;
2017-04-26 13:45:49 +02:00
string userAgentId = GetUniqueIdHash ( ) ;
return new DownloadUpdatesXmlAction ( checkForXenCenter , checkForServerVersion , checkForPatches , userAgent , userAgentId , checkForUpdatesUrl ) ;
}
2017-06-26 23:06:45 +02:00
internal static string GetUniqueIdHash ( )
2017-04-26 13:45:49 +02:00
{
string uniqueIdHash = "nil" ;
try
{
var managementObj = new System . Management . ManagementObject ( "Win32_OperatingSystem=@" ) ;
string serialNumber = ( string ) managementObj [ "SerialNumber" ] ;
if ( ! string . IsNullOrWhiteSpace ( serialNumber ) )
{
var serialBytes = Encoding . ASCII . GetBytes ( serialNumber ) ;
using ( var md = new System . Security . Cryptography . MD5CryptoServiceProvider ( ) ) // MD5 to keep it short enough as this hash is not used for security in any way
{
var hash = md . ComputeHash ( serialBytes ) ;
uniqueIdHash = BitConverter . ToString ( hash ) . Replace ( "-" , "" ) . ToLowerInvariant ( ) ;
}
}
}
catch ( Exception ex )
{
log . Error ( ex ) ;
}
return uniqueIdHash ;
}
2016-11-16 13:26:26 +01:00
/// <summary>
/// It does exactly what CheckForUpdates(true) does, but this is sync and shows an ActionProgressDialog while running
/// </summary>
/// <returns>true if the action has succeeded</returns>
public static bool CheckForUpdatesSync ( Control parentForProgressDialog )
{
if ( Helpers . CommonCriteriaCertificationRelease )
return false ;
2017-04-26 13:45:49 +02:00
var action = CreateDownloadUpdatesXmlAction ( true , true , true , Updates . CheckForUpdatesUrl ) ;
2016-11-16 13:26:26 +01:00
action . Completed + = actionCompleted ;
if ( CheckForUpdatesStarted ! = null )
CheckForUpdatesStarted ( ) ;
using ( var dialog = new ActionProgressDialog ( action , ProgressBarStyle . Marquee ) )
dialog . ShowDialog ( parentForProgressDialog ) ;
return action . Succeeded ;
}
2016-06-07 17:02:43 +02:00
public static string CheckForUpdatesUrl
{
get
{
return Registry . CustomUpdatesXmlLocation ? ? Branding . CheckForUpdatesUrl ;
}
}
2013-11-21 15:27:34 +01:00
private static void actionCompleted ( ActionBase sender )
{
Program . AssertOffEventThread ( ) ;
2013-12-05 13:46:39 +01:00
2013-11-21 15:27:34 +01:00
DownloadUpdatesXmlAction action = sender as DownloadUpdatesXmlAction ;
2013-12-05 13:46:39 +01:00
if ( action = = null )
return ;
2013-11-21 15:27:34 +01:00
2013-12-05 13:46:39 +01:00
bool succeeded = action . Succeeded ;
2013-11-21 15:27:34 +01:00
string errorMessage = string . Empty ;
2013-12-05 13:46:39 +01:00
lock ( updateAlertsLock )
2013-11-21 15:27:34 +01:00
updateAlerts . Clear ( ) ;
2013-12-05 13:46:39 +01:00
if ( succeeded )
{
2014-01-08 14:17:02 +01:00
lock ( downloadedUpdatesLock )
{
2017-01-19 18:51:25 +01:00
XenCenterVersions = action . XenCenterVersions ;
2014-01-08 14:17:02 +01:00
2017-01-19 18:51:25 +01:00
XenServerVersionsForAutoCheck = action . XenServerVersionsForAutoCheck ;
2015-08-04 18:28:29 +02:00
2017-01-19 18:51:25 +01:00
XenServerVersions = action . XenServerVersions ;
2013-11-21 15:27:34 +01:00
2017-01-19 18:51:25 +01:00
XenServerPatches = action . XenServerPatches ;
2014-01-08 14:17:02 +01:00
}
2017-03-14 16:25:49 +01:00
var xenCenterAlerts = NewXenCenterUpdateAlerts ( XenCenterVersions , Program . Version ) ;
if ( xenCenterAlerts ! = null )
updateAlerts . AddRange ( xenCenterAlerts . Where ( a = > ! a . IsDismissed ( ) ) ) ;
2013-11-21 15:27:34 +01:00
2017-03-14 17:38:10 +01:00
var xenServerUpdateAlerts = NewXenServerVersionAlerts ( XenServerVersionsForAutoCheck ) ;
if ( xenServerUpdateAlerts ! = null )
updateAlerts . AddRange ( xenServerUpdateAlerts . Where ( a = > ! a . CanIgnore ) ) ;
2013-12-05 13:46:39 +01:00
2014-01-08 14:17:02 +01:00
var xenServerPatchAlerts = NewXenServerPatchAlerts ( XenServerVersions , XenServerPatches ) ;
2013-12-05 13:46:39 +01:00
if ( xenServerPatchAlerts ! = null )
2014-01-08 14:17:02 +01:00
updateAlerts . AddRange ( xenServerPatchAlerts . Where ( alert = > ! alert . CanIgnore ) ) ;
2013-12-05 13:46:39 +01:00
}
else
{
if ( action . Exception ! = null )
2013-11-21 15:27:34 +01:00
{
2013-12-05 13:46:39 +01:00
if ( action . Exception is System . Net . Sockets . SocketException )
2013-11-21 15:27:34 +01:00
{
2013-12-05 13:46:39 +01:00
errorMessage = Messages . AVAILABLE_UPDATES_NETWORK_ERROR ;
2013-11-21 15:27:34 +01:00
}
2013-12-05 13:46:39 +01:00
else
2013-11-21 15:27:34 +01:00
{
2013-12-05 13:46:39 +01:00
// Clean up and remove excess newlines, carriage returns, trailing nonsense
string errorText = action . Exception . Message . Trim ( ) ;
errorText = System . Text . RegularExpressions . Regex . Replace ( errorText , @"\r\n+" , "" ) ;
errorMessage = string . Format ( Messages . AVAILABLE_UPDATES_ERROR , errorText ) ;
2013-11-21 15:27:34 +01:00
}
}
2013-12-05 13:46:39 +01:00
if ( string . IsNullOrEmpty ( errorMessage ) )
errorMessage = Messages . AVAILABLE_UPDATES_INTERNAL_ERROR ;
2013-11-21 15:27:34 +01:00
}
if ( CheckForUpdatesCompleted ! = null )
CheckForUpdatesCompleted ( succeeded , errorMessage ) ;
}
2014-01-08 14:17:02 +01:00
2013-12-05 13:46:39 +01:00
public static void RegisterCollectionChanged ( CollectionChangeEventHandler handler )
{
updateAlerts . CollectionChanged + = handler ;
}
public static void DeregisterCollectionChanged ( CollectionChangeEventHandler handler )
{
updateAlerts . CollectionChanged - = handler ;
}
2013-06-24 13:41:48 +02:00
2017-03-14 16:25:49 +01:00
public static List < XenCenterUpdateAlert > NewXenCenterUpdateAlerts ( List < XenCenterVersion > xenCenterVersions ,
Version currentProgramVersion )
2013-06-24 13:41:48 +02:00
{
if ( Helpers . CommonCriteriaCertificationRelease )
return null ;
2017-03-14 16:25:49 +01:00
var alerts = new List < XenCenterUpdateAlert > ( ) ;
XenCenterVersion latest = null , latestCr = null ;
2014-01-08 14:17:02 +01:00
if ( xenCenterVersions . Count ! = 0 & & currentProgramVersion ! = new Version ( 0 , 0 , 0 , 0 ) )
{
2017-03-14 16:25:49 +01:00
var latestVersions = from v in xenCenterVersions where v . Latest select v ;
latest = latestVersions . FirstOrDefault ( xcv = > xcv . Lang = = Program . CurrentLanguage ) ? ?
latestVersions . FirstOrDefault ( xcv = > string . IsNullOrEmpty ( xcv . Lang ) ) ;
2014-01-08 14:17:02 +01:00
2017-03-14 16:25:49 +01:00
if ( IsSuitableForXenCenterAlert ( latest , currentProgramVersion ) )
alerts . Add ( new XenCenterUpdateAlert ( latest ) ) ;
2013-06-24 13:41:48 +02:00
2017-03-14 16:25:49 +01:00
var latestCrVersions = from v in xenCenterVersions where v . LatestCr select v ;
latestCr = latestCrVersions . FirstOrDefault ( xcv = > xcv . Lang = = Program . CurrentLanguage ) ? ?
latestCrVersions . FirstOrDefault ( xcv = > string . IsNullOrEmpty ( xcv . Lang ) ) ;
2013-06-24 13:41:48 +02:00
2017-03-14 16:25:49 +01:00
if ( latestCr ! = latest & & IsSuitableForXenCenterAlert ( latestCr , currentProgramVersion ) )
alerts . Add ( new XenCenterUpdateAlert ( latestCr ) ) ;
}
if ( alerts . Count = = 0 )
2013-06-24 13:41:48 +02:00
{
2017-03-14 16:25:49 +01:00
log . Info ( string . Format ( "Not alerting XenCenter update - latest = {0}, latestcr = {1}, detected = {2}" ,
latest ! = null ? latest . VersionAndLang : "" , latestCr ! = null ? latestCr . VersionAndLang : "" , Program . VersionAndLanguage ) ) ;
2013-06-24 13:41:48 +02:00
}
2017-03-14 16:25:49 +01:00
return alerts ;
}
private static bool IsSuitableForXenCenterAlert ( XenCenterVersion toUse , Version currentProgramVersion )
{
if ( toUse = = null )
return false ;
return toUse . Version > currentProgramVersion | |
( toUse . Version = = currentProgramVersion & & toUse . Lang = = Program . CurrentLanguage & &
! PropertyManager . IsCultureLoaded ( Program . CurrentCulture ) ) ;
2013-06-24 13:41:48 +02:00
}
2013-12-05 13:59:38 +01:00
public static List < XenServerPatchAlert > NewXenServerPatchAlerts ( List < XenServerVersion > xenServerVersions ,
2013-12-05 12:34:16 +01:00
List < XenServerPatch > xenServerPatches )
2013-06-24 13:41:48 +02:00
{
if ( Helpers . CommonCriteriaCertificationRelease )
return null ;
2014-01-08 14:17:02 +01:00
var alerts = new List < XenServerPatchAlert > ( ) ;
2013-06-24 13:41:48 +02:00
2017-03-20 11:17:35 +01:00
var xenServerVersionsAsUpdates = xenServerVersions . Where ( v = > v . IsVersionAvailableAsAnUpdate ) ;
2013-06-24 13:41:48 +02:00
foreach ( IXenConnection xenConnection in ConnectionsManager . XenConnectionsCopy )
{
Host master = Helpers . GetMaster ( xenConnection ) ;
Pool pool = Helpers . GetPoolOfOne ( xenConnection ) ;
List < Host > hosts = xenConnection . Cache . Hosts . ToList ( ) ;
if ( master = = null | | pool = = null )
continue ;
2017-03-20 11:44:33 +01:00
var serverVersions = GetServerVersions ( master , xenServerVersions ) ;
2013-06-24 13:41:48 +02:00
if ( serverVersions . Count = = 0 )
continue ;
foreach ( XenServerVersion xenServerVersion in serverVersions )
{
XenServerVersion version = xenServerVersion ;
List < XenServerPatch > patches = xenServerPatches . FindAll ( patch = > version . Patches . Contains ( patch ) ) ;
if ( patches . Count = = 0 )
continue ;
foreach ( XenServerPatch xenServerPatch in patches )
{
2017-03-20 11:17:35 +01:00
XenServerVersion newServerVersion = xenServerVersionsAsUpdates . FirstOrDefault ( newVersion = > newVersion . PatchUuid . Equals ( xenServerPatch . Uuid , StringComparison . OrdinalIgnoreCase ) ) ;
var alert = new XenServerPatchAlert ( xenServerPatch , newServerVersion ) ;
2014-01-08 14:17:02 +01:00
var existingAlert = alerts . Find ( al = > al . Equals ( alert ) ) ;
if ( existingAlert ! = null )
alert = existingAlert ;
else
alerts . Add ( alert ) ;
if ( ! xenConnection . IsConnected )
continue ;
2013-06-24 13:41:48 +02:00
XenServerPatch serverPatch = xenServerPatch ;
2014-02-17 10:31:57 +01:00
2017-03-20 11:44:33 +01:00
var noPatchHosts = hosts . Where ( host = > PatchCanBeInstalledOnHost ( serverPatch , host ) ) ;
2013-12-05 13:03:34 +01:00
if ( noPatchHosts . Count ( ) = = hosts . Count )
2013-06-24 13:41:48 +02:00
alert . IncludeConnection ( xenConnection ) ;
else
alert . IncludeHosts ( noPatchHosts ) ;
}
}
}
return alerts ;
}
2017-03-20 11:44:33 +01:00
private static bool PatchCanBeInstalledOnHost ( XenServerPatch serverPatch , Host host )
{
Debug . Assert ( serverPatch ! = null ) ;
Debug . Assert ( host ! = null ) ;
// A patch can be installed on a host if:
// 1. it is not already installed and
// 2. the host has all the required patches installed and
// 3. the host doesn't have any of the conflicting patches installed
bool elyOrGreater = Helpers . ElyOrGreater ( host ) ;
var appliedUpdates = host . AppliedUpdates ( ) ;
var appliedPatches = host . AppliedPatches ( ) ;
// 1. patch is not already installed
if ( elyOrGreater & & appliedUpdates . Any ( update = > string . Equals ( update . uuid , serverPatch . Uuid , StringComparison . OrdinalIgnoreCase ) ) )
return false ;
if ( ! elyOrGreater & & appliedPatches . Any ( patch = > string . Equals ( patch . uuid , serverPatch . Uuid , StringComparison . OrdinalIgnoreCase ) ) )
return false ;
// 2. the host has all the required patches installed
if ( serverPatch . RequiredPatches ! = null & & serverPatch . RequiredPatches . Count > 0 & &
! serverPatch . RequiredPatches
. All ( requiredPatchUuid = >
elyOrGreater & & appliedUpdates . Any ( update = > string . Equals ( update . uuid , requiredPatchUuid , StringComparison . OrdinalIgnoreCase ) )
| | ! elyOrGreater & & appliedPatches . Any ( patch = > string . Equals ( patch . uuid , requiredPatchUuid , StringComparison . OrdinalIgnoreCase ) )
)
)
return false ;
// 3. the host doesn't have any of the conflicting patches installed
if ( serverPatch . ConflictingPatches ! = null & & serverPatch . ConflictingPatches . Count > 0 & &
serverPatch . ConflictingPatches
. Any ( conflictingPatchUuid = >
elyOrGreater & & appliedUpdates . Any ( update = > string . Equals ( update . uuid , conflictingPatchUuid , StringComparison . OrdinalIgnoreCase ) )
| | ! elyOrGreater & & appliedPatches . Any ( patch = > string . Equals ( patch . uuid , conflictingPatchUuid , StringComparison . OrdinalIgnoreCase ) )
)
)
return false ;
return true ;
}
2017-03-22 11:59:32 +01:00
/// <summary>
/// Returns the latest XenCenter version or null, if the current version is the latest.
/// If a server version is provided, it returns the XenCenter version that is required to work with that server.
/// If no server version is provided it will return the latestCr XenCenter.
/// </summary>
/// <param name="serverVersion"></param>
/// <returns></returns>
public static XenCenterVersion GetRequiredXenCenterVersion ( XenServerVersion serverVersion )
{
if ( XenCenterVersions . Count = = 0 )
return null ;
var currentProgramVersion = Program . Version ;
if ( currentProgramVersion = = new Version ( 0 , 0 , 0 , 0 ) )
return null ;
var latestVersions = from v in XenCenterVersions where v . Latest select v ;
var latest = latestVersions . FirstOrDefault ( xcv = > xcv . Lang = = Program . CurrentLanguage ) ? ?
latestVersions . FirstOrDefault ( xcv = > string . IsNullOrEmpty ( xcv . Lang ) ) ;
var latestCrVersions = from v in XenCenterVersions where v . LatestCr select v ;
var latestCr = latestCrVersions . FirstOrDefault ( xcv = > xcv . Lang = = Program . CurrentLanguage ) ? ?
latestCrVersions . FirstOrDefault ( xcv = > string . IsNullOrEmpty ( xcv . Lang ) ) ;
if ( serverVersion ! = null & & serverVersion . Latest & & latest ! = null )
return latest . Version > currentProgramVersion ? latest : null ;
return latestCr ! = null & & latestCr . Version > currentProgramVersion ? latestCr : null ;
}
2016-02-02 20:12:38 +01:00
2016-06-08 14:34:59 +02:00
/// <summary>
/// This method returns the minimal set of patches for a host if this class already has information about them. Otherwise it returns empty list.
/// Calling this function will not initiate a download or update.
/// </summary>
/// <param name="host"></param>
/// <returns></returns>
public static List < XenServerPatch > RecommendedPatchesForHost ( Host host )
{
var recommendedPatches = new List < XenServerPatch > ( ) ;
if ( XenServerVersions = = null )
2017-08-24 12:54:16 +02:00
return null ;
2016-06-08 14:34:59 +02:00
2017-03-20 11:44:33 +01:00
var serverVersions = GetServerVersions ( host , XenServerVersions ) ;
2016-06-08 14:34:59 +02:00
if ( serverVersions . Count ! = 0 )
{
2016-08-10 11:48:22 +02:00
var minimumPatches = serverVersions [ 0 ] . MinimalPatches ;
2016-06-08 14:34:59 +02:00
2016-08-08 19:30:54 +02:00
if ( minimumPatches = = null ) //unknown
2017-08-24 12:54:16 +02:00
return null ;
2016-08-08 19:30:54 +02:00
2016-10-05 13:55:34 +02:00
bool elyOrGreater = Helpers . ElyOrGreater ( host ) ;
2016-06-08 14:34:59 +02:00
var appliedPatches = host . AppliedPatches ( ) ;
2016-10-05 13:55:34 +02:00
var appliedUpdates = host . AppliedUpdates ( ) ;
if ( elyOrGreater )
{
recommendedPatches = minimumPatches . FindAll ( p = > ! appliedUpdates . Any ( au = > string . Equals ( au . uuid , p . Uuid , StringComparison . OrdinalIgnoreCase ) ) ) ;
}
else
{
recommendedPatches = minimumPatches . FindAll ( p = > ! appliedPatches . Any ( ap = > string . Equals ( ap . uuid , p . Uuid , StringComparison . OrdinalIgnoreCase ) ) ) ;
}
2016-06-08 14:34:59 +02:00
}
return recommendedPatches ;
}
2016-03-24 19:24:12 +01:00
public static UpgradeSequence GetUpgradeSequence ( IXenConnection conn )
2016-02-02 20:12:38 +01:00
{
2016-11-16 14:53:29 +01:00
if ( XenServerVersions = = null )
2016-08-09 17:53:02 +02:00
return null ;
2016-02-02 20:12:38 +01:00
Host master = Helpers . GetMaster ( conn ) ;
if ( master = = null )
return null ;
2016-11-16 14:53:29 +01:00
var version = GetCommonServerVersionOfHostsInAConnection ( conn , XenServerVersions ) ;
2017-03-14 16:25:49 +01:00
2016-08-10 11:48:22 +02:00
if ( version ! = null )
2016-02-02 20:12:38 +01:00
{
2016-08-10 11:48:22 +02:00
if ( version . MinimalPatches = = null )
2016-08-08 19:30:54 +02:00
return null ;
2016-08-10 13:50:45 +02:00
var uSeq = new UpgradeSequence ( ) ;
2017-08-29 17:59:49 +02:00
uSeq . MinimalPatches = new List < XenServerPatch > ( version . MinimalPatches ) ;
// if there is a "new version" update in the update sequence, also add the minimal patches of this new version
if ( uSeq . MinimalPatches . Count > 0 )
{
// assuming that the new version update (if there is one) is the last one in the minimal patches list
var lastUpdate = uSeq . MinimalPatches [ uSeq . MinimalPatches . Count - 1 ] ;
var newServerVersion = XenServerVersions . FirstOrDefault (
v = > v . IsVersionAvailableAsAnUpdate & & v . PatchUuid . Equals ( lastUpdate . Uuid , StringComparison . OrdinalIgnoreCase ) ) ;
if ( newServerVersion ! = null & & newServerVersion . MinimalPatches ! = null )
uSeq . MinimalPatches . AddRange ( newServerVersion . MinimalPatches ) ;
}
2016-02-02 20:12:38 +01:00
2016-08-10 11:48:22 +02:00
List < Host > hosts = conn . Cache . Hosts . ToList ( ) ;
2016-08-10 13:50:45 +02:00
2016-02-02 20:12:38 +01:00
foreach ( Host h in hosts )
{
2016-08-10 11:48:22 +02:00
uSeq [ h ] = GetUpgradeSequenceForHost ( h , uSeq . MinimalPatches ) ;
2016-02-02 20:27:08 +01:00
}
2016-08-09 17:53:02 +02:00
return uSeq ;
2016-02-02 20:27:08 +01:00
}
2016-08-08 19:30:54 +02:00
else
{
return null ;
}
2016-02-02 20:27:08 +01:00
}
2016-02-02 20:12:38 +01:00
2017-03-24 15:11:06 +01:00
/// <summary>
/// Gets an upgrade sequence that contains a version upgrade, optionally followed by the minimal patches for the new version
/// </summary>
/// <param name="conn">Connection for the pool</param>
/// <param name="alert">The alert that refers the version-update</param>
/// <param name="updateTheNewVersion">Also add the minimum patches for the new version (true) or not (false).</param>
/// <returns></returns>
public static UpgradeSequence GetUpgradeSequence ( IXenConnection conn , XenServerPatchAlert alert , bool updateTheNewVersion )
2017-03-24 12:58:03 +01:00
{
2017-03-24 15:11:06 +01:00
Debug . Assert ( conn ! = null ) ;
Debug . Assert ( alert ! = null ) ;
2017-03-24 12:58:03 +01:00
var uSeq = new UpgradeSequence ( ) ;
if ( XenServerVersions = = null )
return null ;
Host master = Helpers . GetMaster ( conn ) ;
if ( master = = null )
return null ;
var version = GetCommonServerVersionOfHostsInAConnection ( conn , XenServerVersions ) ;
2017-03-24 14:53:21 +01:00
// the pool has to be homogeneous
2017-03-24 12:58:03 +01:00
if ( version ! = null )
{
2017-03-24 14:53:21 +01:00
uSeq . MinimalPatches = new List < XenServerPatch > ( ) ;
uSeq . MinimalPatches . Add ( alert . Patch ) ;
2017-03-24 12:58:03 +01:00
2017-03-24 14:53:21 +01:00
// if it's a version updgrade the min sequence will be this patch (the upgrade) and the min patches for the new version
2017-03-24 15:11:06 +01:00
if ( updateTheNewVersion & & alert . NewServerVersion ! = null & & alert . NewServerVersion . MinimalPatches ! = null )
2017-03-24 14:53:21 +01:00
{
2017-03-24 12:58:03 +01:00
uSeq . MinimalPatches . AddRange ( alert . NewServerVersion . MinimalPatches ) ;
}
2017-03-24 15:11:06 +01:00
conn . Cache . Hosts . ToList ( ) . ForEach ( h = >
uSeq [ h ] = GetUpgradeSequenceForHost ( h , uSeq . MinimalPatches )
) ;
2017-03-24 14:53:21 +01:00
return uSeq ;
2017-03-24 12:58:03 +01:00
}
return null ;
}
2016-08-10 11:48:22 +02:00
/// <summary>
2016-08-11 12:14:32 +02:00
/// Returns a XenServerVersion if all hosts of the pool have the same version
2016-08-10 11:48:22 +02:00
/// Returns null if it is unknown or they don't match
/// </summary>
/// <returns></returns>
2016-08-10 13:50:45 +02:00
private static XenServerVersion GetCommonServerVersionOfHostsInAConnection ( IXenConnection connection , List < XenServerVersion > xsVersions )
2016-08-10 11:48:22 +02:00
{
2016-08-10 13:50:45 +02:00
XenServerVersion commonXenServerVersion = null ;
2016-08-10 11:48:22 +02:00
if ( connection = = null )
return null ;
List < Host > hosts = connection . Cache . Hosts . ToList ( ) ;
foreach ( Host host in hosts )
{
2017-03-20 11:44:33 +01:00
var hostVersions = GetServerVersions ( host , xsVersions ) ;
2016-08-10 11:48:22 +02:00
var foundVersion = hostVersions . FirstOrDefault ( ) ;
if ( foundVersion = = null )
{
return null ;
}
else
{
2016-08-10 13:50:45 +02:00
if ( commonXenServerVersion = = null )
2016-08-10 11:48:22 +02:00
{
2016-08-10 13:50:45 +02:00
commonXenServerVersion = foundVersion ;
2016-08-10 11:48:22 +02:00
}
else
{
2016-08-10 13:50:45 +02:00
if ( commonXenServerVersion ! = foundVersion )
2016-08-10 11:48:22 +02:00
return null ;
}
}
}
2016-08-10 13:50:45 +02:00
return commonXenServerVersion ;
2016-08-10 11:48:22 +02:00
}
private static List < XenServerPatch > GetUpgradeSequenceForHost ( Host h , List < XenServerPatch > latestPatches )
2016-02-02 20:27:08 +01:00
{
var sequence = new List < XenServerPatch > ( ) ;
2016-10-05 13:55:34 +02:00
var appliedUpdateUuids = new List < string > ( ) ;
bool elyOrGreater = Helpers . ElyOrGreater ( h ) ;
if ( elyOrGreater )
{
appliedUpdateUuids = h . AppliedUpdates ( ) . Select ( u = > u . uuid ) . ToList ( ) ;
}
else
{
appliedUpdateUuids = h . AppliedPatches ( ) . Select ( p = > p . uuid ) . ToList ( ) ;
}
2016-02-02 20:27:08 +01:00
var neededPatches = new List < XenServerPatch > ( latestPatches ) ;
2016-02-03 13:00:41 +01:00
//Iterate through latestPatches once; in each iteration, move the first item from L0 that has its dependencies met to the end of the Update Schedule (US)
2016-02-02 20:27:08 +01:00
for ( int ii = 0 ; ii < neededPatches . Count ; ii + + )
{
var p = neededPatches [ ii ] ;
//checking requirements
2016-03-31 16:40:23 +02:00
if ( //not applied yet
2016-10-05 13:55:34 +02:00
! appliedUpdateUuids . Any ( apu = > string . Equals ( apu , p . Uuid , StringComparison . OrdinalIgnoreCase ) )
2016-03-31 16:40:23 +02:00
// and either no requirements or they are meet
& & ( p . RequiredPatches = = null
2016-02-02 20:27:08 +01:00
| | p . RequiredPatches . Count = = 0
// all requirements met?
| | p . RequiredPatches . All (
rp = >
//sequence already has the required-patch
2016-03-16 14:46:58 +01:00
( sequence . Count ! = 0 & & sequence . Any ( useqp = > string . Equals ( useqp . Uuid , rp , StringComparison . OrdinalIgnoreCase ) ) )
2016-02-02 20:27:08 +01:00
//the required-patch has already been applied
2016-10-05 13:55:34 +02:00
| | ( appliedUpdateUuids . Count ! = 0 & & appliedUpdateUuids . Any ( apu = > string . Equals ( apu , rp , StringComparison . OrdinalIgnoreCase ) ) )
2016-02-02 20:27:08 +01:00
)
2016-03-31 16:40:23 +02:00
) )
2016-02-02 20:27:08 +01:00
{
// this patch can be added to the upgrade sequence now
2016-03-16 14:41:10 +01:00
sequence . Add ( p ) ;
2016-02-02 20:27:08 +01:00
// by now the patch has either been added to the upgrade sequence or something already contains it among the installed patches
neededPatches . RemoveAt ( ii ) ;
//resetting position - the loop will start on 0. item
ii = - 1 ;
2016-02-02 20:12:38 +01:00
}
}
2016-02-02 20:27:08 +01:00
return sequence ;
2016-02-02 20:12:38 +01:00
}
2016-03-24 19:24:12 +01:00
public class UpgradeSequence : Dictionary < Host , List < XenServerPatch > >
2016-02-02 20:12:38 +01:00
{
2016-03-24 19:24:12 +01:00
private IEnumerable < XenServerPatch > AllPatches
{
get
{
foreach ( var patches in this . Values )
foreach ( var patch in patches )
yield return patch ;
}
}
public List < XenServerPatch > UniquePatches
{
get
{
2016-08-02 17:58:24 +02:00
var uniquePatches = new List < XenServerPatch > ( ) ;
foreach ( var mp in MinimalPatches )
{
if ( AllPatches . Any ( p = > p . Uuid = = mp . Uuid ) )
{
uniquePatches . Add ( mp ) ;
}
}
return uniquePatches ;
2016-03-24 19:24:12 +01:00
}
}
2016-07-19 10:12:30 +02:00
public bool AllHostsUpToDate
{
get
{
if ( this . Count = = 0 )
return false ;
foreach ( var host in this . Keys )
{
if ( this [ host ] . Count > 0 )
return false ;
}
return true ;
}
}
2016-08-02 17:58:24 +02:00
public List < XenServerPatch > MinimalPatches
{
set ;
get ;
}
2016-02-02 20:12:38 +01:00
}
2013-06-24 13:41:48 +02:00
2017-03-14 17:38:10 +01:00
public static List < XenServerVersionAlert > NewXenServerVersionAlerts ( List < XenServerVersion > xenServerVersions )
2013-06-24 13:41:48 +02:00
{
if ( Helpers . CommonCriteriaCertificationRelease )
return null ;
2013-12-05 13:03:34 +01:00
var latestVersion = xenServerVersions . FindAll ( item = > item . Latest ) . OrderByDescending ( v = > v . Version ) . FirstOrDefault ( ) ;
2017-03-14 17:38:10 +01:00
var latestCrVersion = xenServerVersions . FindAll ( item = > item . LatestCr ) . OrderByDescending ( v = > v . Version ) . FirstOrDefault ( ) ;
List < XenServerVersionAlert > alerts = new List < XenServerVersionAlert > ( ) ;
if ( latestVersion ! = null )
alerts . Add ( CreateAlertForXenServerVersion ( latestVersion ) ) ;
2014-01-03 09:37:45 +01:00
2017-03-14 17:38:10 +01:00
if ( latestCrVersion ! = null & & latestCrVersion ! = latestVersion )
alerts . Add ( CreateAlertForXenServerVersion ( latestCrVersion ) ) ;
return alerts ;
}
private static XenServerVersionAlert CreateAlertForXenServerVersion ( XenServerVersion version )
{
var alert = new XenServerVersionAlert ( version ) ;
2014-01-03 09:37:45 +01:00
2017-03-20 11:44:33 +01:00
// the patch that installs this version, if any
var patch = XenServerPatches . FirstOrDefault ( p = > p . Uuid . Equals ( version . PatchUuid , StringComparison . OrdinalIgnoreCase ) ) ;
2013-06-24 13:41:48 +02:00
foreach ( IXenConnection xc in ConnectionsManager . XenConnectionsCopy )
{
2014-01-08 14:17:02 +01:00
if ( ! xc . IsConnected )
continue ;
2013-06-24 13:41:48 +02:00
Host master = Helpers . GetMaster ( xc ) ;
Pool pool = Helpers . GetPoolOfOne ( xc ) ;
List < Host > hosts = xc . Cache . Hosts . ToList ( ) ;
if ( master = = null | | pool = = null )
continue ;
2017-03-20 11:44:33 +01:00
// Show the Upgrade alert for a host if:
// - the host version is older than this version AND
// - there is no patch (amongst the current version patches) that can update to this version OR, if there is a patch, the patch cannot be installed
var patchApplicable = patch ! = null & & GetServerVersions ( master , XenServerVersions ) . Any ( v = > v . Patches . Contains ( patch ) ) ;
var outOfDateHosts = hosts . Where ( host = > new Version ( Helpers . HostProductVersion ( host ) ) < version . Version
& & ( ! patchApplicable | | ! PatchCanBeInstalledOnHost ( patch , host ) ) ) ;
2013-06-24 13:41:48 +02:00
2013-12-05 13:03:34 +01:00
if ( outOfDateHosts . Count ( ) = = hosts . Count )
2013-06-24 13:41:48 +02:00
alert . IncludeConnection ( xc ) ;
else
alert . IncludeHosts ( outOfDateHosts ) ;
}
return alert ;
}
2013-12-05 13:46:39 +01:00
2017-10-03 16:32:57 +02:00
public static List < XenServerVersion > GetServerVersions ( Host host , List < XenServerVersion > xenServerVersions )
2017-03-20 11:44:33 +01:00
{
var serverVersions = xenServerVersions . FindAll ( version = >
{
if ( version . BuildNumber ! = string . Empty )
2017-09-29 12:56:46 +02:00
return ( host . BuildNumberRaw ( ) = = version . BuildNumber ) ;
2017-03-20 11:44:33 +01:00
return Helpers . HostProductVersionWithOEM ( host ) = = version . VersionAndOEM
| | ( version . Oem ! = null & & Helpers . OEMName ( host ) . StartsWith ( version . Oem )
& & Helpers . HostProductVersion ( host ) = = version . Version . ToString ( ) ) ;
} ) ;
return serverVersions ;
}
2014-01-08 14:17:02 +01:00
2013-12-05 13:46:39 +01:00
public static void CheckServerVersion ( )
{
2017-03-14 17:38:10 +01:00
var alerts = NewXenServerVersionAlerts ( XenServerVersionsForAutoCheck ) ;
if ( alerts = = null | | alerts . Count = = 0 )
2013-12-05 13:46:39 +01:00
return ;
2017-03-14 17:38:10 +01:00
alerts . ForEach ( a = > CheckUpdate ( a ) ) ;
2013-12-05 13:46:39 +01:00
}
public static void CheckServerPatches ( )
{
2014-01-08 14:17:02 +01:00
var alerts = NewXenServerPatchAlerts ( XenServerVersions , XenServerPatches ) ;
if ( alerts = = null )
2013-12-05 13:46:39 +01:00
return ;
2017-03-14 17:38:10 +01:00
alerts . ForEach ( a = > CheckUpdate ( a ) ) ;
2014-01-08 14:17:02 +01:00
}
private static void CheckUpdate ( XenServerUpdateAlert alert )
{
var existingAlert = FindUpdate ( alert ) ;
if ( existingAlert ! = null & & alert . CanIgnore )
RemoveUpdate ( existingAlert ) ;
else if ( existingAlert ! = null )
( ( XenServerUpdateAlert ) existingAlert ) . CopyConnectionsAndHosts ( alert ) ;
else if ( ! alert . CanIgnore )
AddUpate ( alert ) ;
2013-12-05 13:46:39 +01:00
}
2015-07-23 14:37:14 +02:00
public static void RestoreDismissedUpdates ( )
{
2016-08-08 15:37:47 +02:00
var actions = new List < AsyncAction > ( ) ;
foreach ( IXenConnection connection in ConnectionsManager . XenConnectionsCopy )
actions . Add ( new RestoreDismissedUpdatesAction ( connection ) ) ;
2015-07-30 15:58:23 +02:00
2016-08-08 15:37:47 +02:00
var action = new ParallelAction ( Messages . RESTORE_DISMISSED_UPDATES , Messages . RESTORING , Messages . COMPLETED , actions , true , false ) ;
action . Completed + = action_Completed ;
2015-07-23 14:37:14 +02:00
2016-08-08 15:37:47 +02:00
if ( RestoreDismissedUpdatesStarted ! = null )
RestoreDismissedUpdatesStarted ( ) ;
2015-07-23 14:37:14 +02:00
2016-08-08 15:37:47 +02:00
action . RunAsync ( ) ;
2015-07-23 14:37:14 +02:00
}
2015-11-05 16:49:38 +01:00
2016-08-08 15:37:47 +02:00
private static void action_Completed ( ActionBase action )
2015-11-05 16:49:38 +01:00
{
2016-08-08 15:37:47 +02:00
Program . Invoke ( Program . MainWindow , ( ) = >
2015-11-05 16:49:38 +01:00
{
2016-08-08 15:37:47 +02:00
Properties . Settings . Default . LatestXenCenterSeen = "" ;
Settings . TrySaveSettings ( ) ;
CheckForUpdates ( true ) ;
} ) ;
2015-11-05 16:49:38 +01:00
}
2013-06-24 13:41:48 +02:00
}
}