xenadmin/XenModel/Actions/Host/ApplyLicenseEditionAction.cs
Michael Z 816bd0601a CP-29230: Switch between new and legacy license names according to server version.
Signed-off-by: Michael Z <michael.zhao@citrix.com>
2018-09-13 10:39:14 +01:00

256 lines
11 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.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;
public List<LicenseFailure> LicenseFailures { get; private set; }
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)
{
LicenseFailures = new List<LicenseFailure>();
Util.ThrowIfEnumerableParameterNullOrEmpty(xos, "xenobjects");
_edition = edition;
this.xos = xos;
_licenseServerAddress = licenseServerAddress;
_licenseServerPort = licenseServerPort;
this.DoOnLicensingFailure = DoOnLicensingFailure;
if (xos != null && Host == null && Pool == null && xos.Count() == 1)
{
var xo = xos.FirstOrDefault();
if (xo is Host)
Host = xo as Host;
else if (xo is Pool)
Pool = xo as Pool;
}
}
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()
{
// 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;
Message.MessageType messageType;
// if this is a message alert, its Name property will contain the MessageType
if (host != null && host.uuid == alert.HostUuid && Enum.TryParse(alert.Name, out messageType))
{
switch (messageType)
{
case Message.MessageType.LICENSE_NOT_AVAILABLE:
case Message.MessageType.LICENSE_SERVER_UNREACHABLE:
case Message.MessageType.LICENSE_SERVER_VERSION_OBSOLETE:
alertText = string.Format(Message.FriendlyBody(alert.Name), xoClosure.Name());
break;
case Message.MessageType.GRACE_LICENSE:
alertText = string.Empty;
break;
}
}
}
}
};
Alert.RegisterAlertCollectionChanged(alertsChangeHandler);
// 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);
if(xo is Host && host != null)
{
Host.apply_edition(host.Connection.Session, host.opaque_ref, host.GetEditionText(_edition), false);
// PR-1102: populate the list of updated hosts
updatedHosts.Add(host, previousLicenseData);
}
if (xo is Pool)
{
Pool.apply_edition(xo.Connection.Session, pool.opaque_ref, xo.Connection.Cache.Hosts.First().GetEditionText(_edition));
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
{
Alert.DeregisterAlertCollectionChanged(alertsChangeHandler);
}
}
// PR-1102: Send licensing data to the activation server
if (updatedHosts.Count > 0)
{
LicensingHelper.SendLicenseEditionData(updatedHosts, updatedHosts.Keys.First().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;
}
}
}