2013-06-24 13:41:48 +02:00
/ * 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.Linq ;
using System.Collections.Generic ;
using XenAPI ;
using XenAdmin.Alerts ;
using System.ComponentModel ;
using System.Threading ;
using XenAdmin.Core ;
using XenAdmin.Actions.HostActions ;
namespace XenAdmin.Actions
{
/// <summary>
/// This Action is for v6 (Midnight Ride) licensing. It sets the license server details and sets the edition to advanced, enterprise, enterprise-xd, platinum or free.
/// </summary>
public class ApplyLicenseEditionAction : AsyncAction
{
private readonly IEnumerable < IXenObject > xos ;
private readonly Host . Edition _edition ;
private readonly string _licenseServerAddress ;
private readonly string _licenseServerPort ;
protected readonly Action < List < LicenseFailure > , string > DoOnLicensingFailure ;
/// <summary>
/// Initializes a new instance of the <see cref="ApplyLicenseEditionAction"/> class.
/// </summary>
/// <param name="xos">The hosts for which the license should be set.</param>
/// <param name="edition">The edition the hosts should be set to.</param>
/// <param name="licenseServerAddress">The license server address.</param>
/// <param name="licenseServerPort">The license server port.</param>
/// <param name="DoOnLicensingFailure">The method to call when the licensing fails. This method will show failure details in a dialog</param>
public ApplyLicenseEditionAction ( IEnumerable < IXenObject > xos , Host . Edition edition , string licenseServerAddress , string licenseServerPort ,
Action < List < LicenseFailure > , string > DoOnLicensingFailure )
: base ( null , Messages . LICENSE_UPDATING_LICENSES )
{
Util . ThrowIfEnumerableParameterNullOrEmpty ( xos , "xenobjects" ) ;
_edition = edition ;
this . xos = xos ;
_licenseServerAddress = licenseServerAddress ;
_licenseServerPort = licenseServerPort ;
this . DoOnLicensingFailure = DoOnLicensingFailure ;
}
private static void SetLicenseServer ( Host host , string licenseServerAddress , string licenseServerPort )
{
Dictionary < string , string > d = new Dictionary < string , string > ( ) ;
if ( ! string . IsNullOrEmpty ( licenseServerAddress ) & & ! string . IsNullOrEmpty ( licenseServerPort ) )
{
d . Add ( "address" , licenseServerAddress ) ;
d . Add ( "port" , licenseServerPort ) ;
}
XenAPI . Host . set_license_server ( host . Connection . Session , host . opaque_ref , d ) ;
}
protected override void Run ( )
{
List < LicenseFailure > licenseFailures = new List < LicenseFailure > ( ) ;
// PR-1102: hosts that have been updated, plus the previous edition information - this data will be sent to the licensing server
Dictionary < Host , LicensingHelper . LicenseDataStruct > updatedHosts = new Dictionary < Host , LicensingHelper . LicenseDataStruct > ( ) ;
this . Description = Messages . LICENSE_UPDATING_LICENSES ;
foreach ( IXenObject xo in xos )
{
Connection = xo . Connection ;
if ( ! Connection . IsConnected )
{
continue ;
}
Host host = null ;
Pool pool = null ;
if ( xo is Host )
host = xo as Host ;
if ( xo is Pool )
{
pool = xo as Pool ;
host = xo . Connection . Resolve ( pool . master ) ;
}
string previousLicenseServerAddress = null ;
string previousLicenseServerPort = null ;
CollectionChangeEventHandler alertsChangeHandler = null ;
string alertText = null ;
object lck = new object ( ) ;
if ( host ! = null & & host . license_server . ContainsKey ( "address" ) )
{
previousLicenseServerAddress = host . license_server [ "address" ] ;
}
if ( host ! = null & & host . license_server . ContainsKey ( "port" ) )
{
previousLicenseServerPort = host . license_server [ "port" ] ;
}
try
{
if ( pool ! = null )
pool . Connection . Cache . Hosts . ToList ( ) . ForEach ( h = > SetLicenseServer ( h , _licenseServerAddress , _licenseServerPort ) ) ;
else
SetLicenseServer ( host , _licenseServerAddress , _licenseServerPort ) ;
IXenObject xoClosure = xo ;
alertsChangeHandler = delegate ( object sender , CollectionChangeEventArgs e )
{
if ( e . Action = = CollectionChangeAction . Add )
{
lock ( lck )
{
Alert alert = ( Alert ) e . Element ;
if ( host ! = null & & host . uuid = = alert . HostUuid )
{
if ( alert . Title = = PropertyManager . GetFriendlyName ( "Message.name-license_not_available" ) )
{
// the license server reported there were no licenses available.
alertText = string . Format ( PropertyManager . GetFriendlyName ( "Message.body-license_not_available" ) , xoClosure . Name ) ;
}
else if ( alert . Title = = PropertyManager . GetFriendlyName ( "Message.name-license_server_unreachable" ) )
{
// couldn't check out license because couldn't contact license server
alertText = string . Format ( PropertyManager . GetFriendlyName ( "Message.body-license_server_unreachable" ) , xoClosure . Name ) ;
}
else if ( alert . Title = = PropertyManager . GetFriendlyName ( "Message.name-grace_license" ) )
{
alertText = string . Empty ;
}
}
}
}
} ;
2013-11-21 12:57:57 +01:00
Alert . RegisterAlertCollectionChanged ( alertsChangeHandler ) ;
2013-06-24 13:41:48 +02:00
// PR-1102: catch the host's license data, before applying the new one, so it can be sent later to the licensing server
LicensingHelper . LicenseDataStruct previousLicenseData = new LicensingHelper . LicenseDataStruct ( host ) ;
2013-07-19 17:32:42 +02:00
if ( xo is Host & & host ! = null )
2013-06-24 13:41:48 +02:00
{
2013-08-05 18:43:51 +02:00
if ( Helpers . ClearwaterOrGreater ( host ) )
Host . apply_edition ( host . Connection . Session , host . opaque_ref , Host . GetEditionText ( _edition ) , false ) ;
2013-07-19 17:32:42 +02:00
else
Host . apply_edition ( host . Connection . Session , host . opaque_ref , Host . GetEditionText ( _edition ) ) ;
2013-06-24 13:41:48 +02:00
// PR-1102: populate the list of updated hosts
updatedHosts . Add ( host , previousLicenseData ) ;
}
if ( xo is Pool )
{
if ( ! Helpers . ClearwaterOrGreater ( xo . Connection ) )
{
foreach ( Host poolHost in xo . Connection . Cache . Hosts )
{
2013-07-19 17:32:42 +02:00
Host . apply_edition ( host . Connection . Session , poolHost . opaque_ref , Host . GetEditionText ( _edition ) ) ;
2013-06-24 13:41:48 +02:00
}
}
else
{
Pool . apply_edition ( xo . Connection . Session , pool . opaque_ref , Host . GetEditionText ( _edition ) ) ;
//TODO: Look into why this is required. Event.Next returning late? XAPI returning before updated?
Thread . Sleep ( 200 ) ;
}
xo . Connection . Cache . Hosts . ToList ( ) . ForEach ( h = > updatedHosts . Add ( h , previousLicenseData ) ) ;
}
Description = Messages . APPLYLICENSE_UPDATED ;
}
catch ( Failure e )
{
for ( int i = 0 ; i < 50 ; i + + )
{
Thread . Sleep ( 100 ) ;
lock ( lck )
{
if ( alertText ! = null )
break ;
}
}
licenseFailures . Add ( new LicenseFailure ( host , alertText ? ? e . Message ) ) ;
if ( pool ! = null )
pool . Connection . Cache . Hosts . ToList ( ) . ForEach ( h = > SetLicenseServer ( h , previousLicenseServerAddress , previousLicenseServerPort ) ) ;
else
SetLicenseServer ( host , previousLicenseServerAddress , previousLicenseServerPort ) ;
}
finally
{
2013-11-21 12:57:57 +01:00
Alert . DeregisterAlertCollectionChanged ( alertsChangeHandler ) ;
2013-06-24 13:41:48 +02:00
}
}
// PR-1102: Send licensing data to the activation server
if ( updatedHosts . Count > 0 )
{
LicensingHelper . SendLicenseEditionData ( updatedHosts , Host . GetEditionText ( _edition ) ) ;
}
if ( licenseFailures . Count > 0 )
{
string exceptionText = licenseFailures . Count = = 1 ? string . Format ( Messages . LICENSE_ERROR_1 , licenseFailures [ 0 ] . Host . Name ) : string . Format ( Messages . LICENSE_ERROR_MANY , licenseFailures . Count , new List < IXenObject > ( xos ) . Count ) ;
if ( DoOnLicensingFailure ! = null )
DoOnLicensingFailure ( licenseFailures , exceptionText ) ;
throw new InvalidOperationException ( exceptionText ) ;
}
}
}
public class LicenseFailure
{
public Host Host ;
public string AlertText ;
public LicenseFailure ( Host host , string alertText )
{
Host = host ;
AlertText = alertText ;
}
}
}