From a8fe293c3e4eec163d11f2caa6da18dea0336394 Mon Sep 17 00:00:00 2001 From: Konstantina Chremmou Date: Mon, 18 Sep 2023 18:47:39 +0100 Subject: [PATCH 1/4] CA-383040/XSI-1500: Resume WLB when reverting resolved actions after an update or upgrade. Signed-off-by: Konstantina Chremmou --- .../Problems/PoolProblem/WLBEnabledProblem.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs b/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs index db23d4cdb2..2168e224ce 100644 --- a/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs +++ b/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs @@ -66,6 +66,23 @@ protected override AsyncAction CreateAction(out bool cancelled) }, true); } + + public override AsyncAction CreateUnwindChangesAction() + { + var pool = Pool.Connection.Resolve(new XenRef(Pool.opaque_ref)); + + if (pool == null) + { + foreach (var xenConnection in ConnectionsManager.XenConnectionsCopy) + { + pool = xenConnection.Resolve(new XenRef(Pool.opaque_ref)); + if (pool != null) + break; + } + } + + return pool == null ? null : new EnableWLBAction(pool); + } } class WLBEnabledWarning : Warning From 884160e779f592b914cf2470932e77d5592e8ba3 Mon Sep 17 00:00:00 2001 From: Konstantina Chremmou Date: Mon, 18 Sep 2023 22:53:39 +0100 Subject: [PATCH 2/4] Simplified the action resolving the WLB-on problem. Also: Fixed exception handling to avoid unnecessary exception rethrowing and possible cast crash. Signed-off-by: Konstantina Chremmou --- .../Problems/PoolProblem/WLBEnabledProblem.cs | 16 +---- XenModel/Actions/WLB/DisableWLBAction.cs | 71 +++++++++---------- 2 files changed, 33 insertions(+), 54 deletions(-) diff --git a/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs b/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs index 2168e224ce..1d87ea36fb 100644 --- a/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs +++ b/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs @@ -28,10 +28,8 @@ * SUCH DAMAGE. */ -using System.Threading; using XenAdmin.Actions; using XenAdmin.Actions.Wlb; -using XenAdmin.Core; using XenAdmin.Diagnostics.Checks; using XenAPI; @@ -52,19 +50,7 @@ public WLBEnabledProblem(Check check, Pool pool) protected override AsyncAction CreateAction(out bool cancelled) { cancelled = false; - return new DelegatedAsyncAction(Pool.Connection, Messages.HELP_MESSAGE_DISABLE_WLB, "", "", - ss => - { - var action = new DisableWLBAction(Pool, false); - action.RunSync(ss); - int count = 0; - while (Helpers.WlbEnabled(Pool.Connection) && count < 10) - { - Thread.Sleep(500); - count++; - } - }, true); - + return new DisableWLBAction(Pool, false); } public override AsyncAction CreateUnwindChangesAction() diff --git a/XenModel/Actions/WLB/DisableWLBAction.cs b/XenModel/Actions/WLB/DisableWLBAction.cs index 60ff88b4ee..185fb9a05a 100644 --- a/XenModel/Actions/WLB/DisableWLBAction.cs +++ b/XenModel/Actions/WLB/DisableWLBAction.cs @@ -39,24 +39,23 @@ namespace XenAdmin.Actions.Wlb public class DisableWLBAction : AsyncAction { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - private bool _deconfigure = false; + private readonly bool _deconfigure; private static string OPTIMIZINGPOOL = "wlb_optimizing_pool"; public DisableWLBAction(Pool pool, bool deconfigure) : base(pool.Connection, string.Format(Messages.DISABLING_WLB_ON, Helpers.GetName(pool).Ellipsise(50)), Messages.DISABLING_WLB, false) { - this.Pool = pool; - this._deconfigure = deconfigure; + Pool = pool; + _deconfigure = deconfigure; if (deconfigure) { - this.Title = String.Format(Messages.DECONFIGURING_WLB_ON, Helpers.GetName(pool).Ellipsise(50)); - this.Description = Messages.DECONFIGURING_WLB; + Title = string.Format(Messages.DECONFIGURING_WLB_ON, Helpers.GetName(pool).Ellipsise(50)); + Description = Messages.DECONFIGURING_WLB; } #region RBAC Dependencies - ApiMethodsToRoleCheck.Add("pool.set_wlb_enabled"); - ApiMethodsToRoleCheck.Add("pool.deconfigure_wlb"); + ApiMethodsToRoleCheck.AddRange("pool.set_wlb_enabled", "pool.deconfigure_wlb"); #endregion } @@ -66,31 +65,27 @@ protected override void Run() { try { - if (!Helpers.WlbEnabled(Pool.Connection)) + if (!Pool.wlb_enabled) { log.Debug("Resuming WLB (prior to disconnecting) for pool " + Pool.Name()); - XenAPI.Pool.set_wlb_enabled(this.Session, Pool.opaque_ref, true); + Pool.set_wlb_enabled(Session, Pool.opaque_ref, true); log.Debug("Success resuming WLB on pool " + Pool.Name()); } - log.Debug("Disconnecting Workload Balancing from pool " + Pool.Name() + " and removing all pool data"); - XenAPI.Pool.deconfigure_wlb(this.Session); + + log.Debug($"Disconnecting Workload Balancing from pool {Pool.Name()} and removing all pool data"); + Pool.deconfigure_wlb(Session); log.Debug("Success disconnecting Workload Balancing on pool " + Pool.Name()); - this.Description = Messages.COMPLETED; - WlbServerState.SetState(this.Session, Pool, WlbServerState.ServerState.NotConfigured); - } - catch (Exception ex) - { - //Force disabling of WLB - XenAPI.Pool.set_wlb_enabled(this.Session, Pool.opaque_ref, false); - WlbServerState.SetState(this.Session, Pool, WlbServerState.ServerState.ConnectionError, (Failure)ex); - log.Debug($"Disconnecting Workload Balancing failed on pool {Pool.Name()}. Workload Balancing has been paused.", ex); - throw new Exception(string.Format(Messages.ACTION_WLB_DECONFIGURE_FAILED, Pool.Name(), ex.Message)); + WlbServerState.SetState(Session, Pool, WlbServerState.ServerState.NotConfigured); + Description = Messages.COMPLETED; } - finally + catch (Failure f) { - //Clear the Optimizing Pool flag in case it was left behind - Helpers.SetOtherConfig(this.Session, this.Pool, OPTIMIZINGPOOL, string.Empty); + Pool.set_wlb_enabled(Session, Pool.opaque_ref, false); + WlbServerState.SetState(Session, Pool, WlbServerState.ServerState.ConnectionError, f); + log.Debug($"Disconnecting Workload Balancing failed on pool {Pool.Name()}. Workload Balancing has been paused.", f); + + throw new Exception(string.Format(Messages.ACTION_WLB_DECONFIGURE_FAILED, Pool.Name(), f)); } } else @@ -98,27 +93,25 @@ protected override void Run() try { log.Debug("Pausing Workload Balancing on pool " + Pool.Name()); - XenAPI.Pool.set_wlb_enabled(this.Session, Pool.opaque_ref, false); + Pool.set_wlb_enabled(Session, Pool.opaque_ref, false); log.Debug("Success pausing Workload Balancing on pool " + Pool.Name()); - this.Description = Messages.COMPLETED; - WlbServerState.SetState(this.Session, Pool, WlbServerState.ServerState.Disabled); - } - catch (Exception ex) - { - if (ex is Failure) - { - WlbServerState.SetState(this.Session, Pool, WlbServerState.ServerState.ConnectionError, (Failure)ex); - } - throw ex; + + Connection.WaitFor(() => !Pool.wlb_enabled, null); + WlbServerState.SetState(Session, Pool, WlbServerState.ServerState.Disabled); + Description = Messages.COMPLETED; } - finally + catch (Failure f) { - //Clear the Optimizing Pool flag in case it was left behind - Helpers.SetOtherConfig(this.Session, this.Pool, OPTIMIZINGPOOL, string.Empty); + WlbServerState.SetState(Session, Pool, WlbServerState.ServerState.ConnectionError, f); + throw; } } - } + protected override void Clean() + { + //Clear the Optimizing Pool flag in case it was left behind + Helpers.SetOtherConfig(Session, Pool, OPTIMIZINGPOOL, string.Empty); + } } } From ff0ce939f75056cd18b810a0815e9ea60ff089c7 Mon Sep 17 00:00:00 2001 From: Konstantina Chremmou Date: Tue, 19 Sep 2023 22:22:14 +0100 Subject: [PATCH 3/4] Separated HA and WLB prechecks so the problems can be resolved at the same time. Signed-off-by: Konstantina Chremmou --- .../Checks/AssertCanEvacuateCheck.cs | 18 +++------ XenAdmin/Diagnostics/Checks/HaWlbOffCheck.cs | 29 +++++++++----- .../Problems/PoolProblem/HAEnabledProblem.cs | 29 ++++++++++---- .../Problems/PoolProblem/WLBEnabledProblem.cs | 17 -------- .../PatchingWizard_PrecheckPage.cs | 11 +++-- .../RollingUpgradeWizardPrecheckPage.cs | 14 ++++--- XenModel/Messages.Designer.cs | 40 ++++++++++++++----- XenModel/Messages.resx | 14 +++++-- 8 files changed, 102 insertions(+), 70 deletions(-) diff --git a/XenAdmin/Diagnostics/Checks/AssertCanEvacuateCheck.cs b/XenAdmin/Diagnostics/Checks/AssertCanEvacuateCheck.cs index d7037f0178..79fe332ba9 100644 --- a/XenAdmin/Diagnostics/Checks/AssertCanEvacuateCheck.cs +++ b/XenAdmin/Diagnostics/Checks/AssertCanEvacuateCheck.cs @@ -258,16 +258,11 @@ private static CannotMigrateVM.CannotMigrateVMReason GetMoreSpecificReasonForCan protected override Problem RunHostCheck() { Pool pool = Helpers.GetPool(Host.Connection); - if (pool != null) - { - if (pool.ha_enabled) - return new HAEnabledWarning(this, pool, Host); - - if (Helpers.WlbEnabled(pool.Connection)) - return new WLBEnabledWarning(this, pool, Host); - } - return null; + if (pool == null || (!pool.ha_enabled && !pool.wlb_enabled)) + return null; + + return new HaWlbEnabledWarning(this, pool, Host); } public override List RunAllChecks() @@ -279,9 +274,6 @@ public override List RunAllChecks() return CheckHost(); } - public override string Description - { - get { return Messages.ASSERT_CAN_EVACUATE_CHECK_DESCRIPTION; } - } + public override string Description => Messages.ASSERT_CAN_EVACUATE_CHECK_DESCRIPTION; } } diff --git a/XenAdmin/Diagnostics/Checks/HaWlbOffCheck.cs b/XenAdmin/Diagnostics/Checks/HaWlbOffCheck.cs index 84c9d6c31e..b82e35083a 100644 --- a/XenAdmin/Diagnostics/Checks/HaWlbOffCheck.cs +++ b/XenAdmin/Diagnostics/Checks/HaWlbOffCheck.cs @@ -28,7 +28,6 @@ * SUCH DAMAGE. */ -using XenAdmin.Core; using XenAdmin.Diagnostics.Problems; using XenAdmin.Diagnostics.Problems.PoolProblem; using XenAPI; @@ -36,25 +35,37 @@ namespace XenAdmin.Diagnostics.Checks { - class HaWlbOffCheck : PoolCheck + internal class HaOffCheck : PoolCheck { - public HaWlbOffCheck(Pool pool) + public HaOffCheck(Pool pool) : base(pool) { } protected override Problem RunCheck() { - if (Pool.ha_enabled) - return new HAEnabledProblem(this, Pool); + return Pool.ha_enabled ? new HAEnabledProblem(this, Pool) : null; + } + + public override string Description => Messages.HA_CHECK_DESCRIPTION; + + public override string SuccessfulCheckDescription => string.Format(Messages.PATCHING_WIZARD_CHECK_ON_XENOBJECT_OK, Pool.Name(), Description); + } + - if (Helpers.WlbEnabled(Pool.Connection)) - return new WLBEnabledProblem(this, Pool); + internal class WlbOffCheck : PoolCheck + { + public WlbOffCheck(Pool pool) + : base(pool) + { + } - return null; + protected override Problem RunCheck() + { + return Pool.wlb_enabled ? new WLBEnabledProblem(this, Pool) : null; } - public override string Description => Messages.HA_WLB_CHECK_DESCRIPTION; + public override string Description => Messages.WLB_CHECK_DESCRIPTION; public override string SuccessfulCheckDescription => string.Format(Messages.PATCHING_WIZARD_CHECK_ON_XENOBJECT_OK, Pool.Name(), Description); } diff --git a/XenAdmin/Diagnostics/Problems/PoolProblem/HAEnabledProblem.cs b/XenAdmin/Diagnostics/Problems/PoolProblem/HAEnabledProblem.cs index 2e015f1ca3..6e129a5312 100644 --- a/XenAdmin/Diagnostics/Problems/PoolProblem/HAEnabledProblem.cs +++ b/XenAdmin/Diagnostics/Problems/PoolProblem/HAEnabledProblem.cs @@ -86,20 +86,35 @@ public DrHAEnabledProblem(Check check, Pool pool) public override string Description => Messages.DR_WIZARD_PROBLEM_HA_ENABLED; } - class HAEnabledWarning : Warning + internal class HaWlbEnabledWarning : Warning { - private readonly Pool pool; - private readonly Host host; + private readonly Pool _pool; + private readonly Host _host; - public HAEnabledWarning(Check check, Pool pool, Host host) + public HaWlbEnabledWarning(Check check, Pool pool, Host host) : base(check) { - this.pool = pool; - this.host = host; + _pool = pool; + _host = host; } public override string Title => Check.Description; - public override string Description => string.Format(Messages.UPDATES_WIZARD_HA_ON_WARNING, host, pool); + public override string Description + { + get + { + if (_pool.ha_enabled && _pool.wlb_enabled) + return string.Format(Messages.UPDATES_WIZARD_HA_AND_WLB_ON_WARNING, _host, _pool); + + if (_pool.ha_enabled) + return string.Format(Messages.UPDATES_WIZARD_HA_ON_WARNING, _host, _pool); + + if (_pool.wlb_enabled) + return string.Format(Messages.UPDATES_WIZARD_WLB_ON_WARNING, _host, _pool); + + return string.Empty; + } + } } } diff --git a/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs b/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs index 1d87ea36fb..24b3179a54 100644 --- a/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs +++ b/XenAdmin/Diagnostics/Problems/PoolProblem/WLBEnabledProblem.cs @@ -70,21 +70,4 @@ public override AsyncAction CreateUnwindChangesAction() return pool == null ? null : new EnableWLBAction(pool); } } - - class WLBEnabledWarning : Warning - { - private readonly Pool pool; - private readonly Host host; - - public WLBEnabledWarning(Check check, Pool pool, Host host) - : base(check) - { - this.pool = pool; - this.host = host; - } - - public override string Title => Check.Description; - - public override string Description => string.Format(Messages.UPDATES_WIZARD_WLB_ON_WARNING, host, pool); - } } diff --git a/XenAdmin/Wizards/PatchingWizard/PatchingWizard_PrecheckPage.cs b/XenAdmin/Wizards/PatchingWizard/PatchingWizard_PrecheckPage.cs index 4bef97a927..e78395cf37 100644 --- a/XenAdmin/Wizards/PatchingWizard/PatchingWizard_PrecheckPage.cs +++ b/XenAdmin/Wizards/PatchingWizard/PatchingWizard_PrecheckPage.cs @@ -359,13 +359,16 @@ private List GenerateCommonChecks(List applicableServers) groups.Add(new CheckGroup(Messages.CHECKING_HOST_LIVENESS_STATUS, livenessChecks)); - //HA checks + //HA and WLB checks - var haChecks = new List(); + var haWlbChecks = new List(); foreach (Pool pool in SelectedPools) - haChecks.Add(new HaWlbOffCheck(pool)); + { + haWlbChecks.Add(new HaOffCheck(pool)); + haWlbChecks.Add(new WlbOffCheck(pool)); + } - groups.Add(new CheckGroup(Messages.CHECKING_HA_STATUS, haChecks)); + groups.Add(new CheckGroup(Messages.CHECKING_HA_AND_WLB_STATUS, haWlbChecks)); //PBDsPluggedCheck var pbdChecks = new List(); diff --git a/XenAdmin/Wizards/RollingUpgradeWizard/RollingUpgradeWizardPrecheckPage.cs b/XenAdmin/Wizards/RollingUpgradeWizard/RollingUpgradeWizardPrecheckPage.cs index 877dd915b3..b9d9a3940e 100644 --- a/XenAdmin/Wizards/RollingUpgradeWizard/RollingUpgradeWizardPrecheckPage.cs +++ b/XenAdmin/Wizards/RollingUpgradeWizard/RollingUpgradeWizardPrecheckPage.cs @@ -259,12 +259,16 @@ where check.CanRun() if (pvChecks.Count > 0) groups.Add(new CheckGroup(Messages.CHECKING_PV_GUESTS, pvChecks)); - //HA checks - for each pool - var haChecks = (from Pool pool in SelectedPools - select new HaWlbOffCheck(pool) as Check).ToList(); + //HA and WLB checks - for each pool + var haWlbChecks = new List(); + foreach (var pool in SelectedPools) + { + haWlbChecks.Add(new HaOffCheck(pool)); + haWlbChecks.Add(new WlbOffCheck(pool)); + } - if (haChecks.Count > 0) - groups.Add(new CheckGroup(Messages.CHECKING_HA_STATUS, haChecks)); + if (haWlbChecks.Count > 0) + groups.Add(new CheckGroup(Messages.CHECKING_HA_AND_WLB_STATUS, haWlbChecks)); //Checking can evacuate host - for hosts that will be upgraded or updated var evacuateChecks = (from Host host in hostsToUpgradeOrUpdate diff --git a/XenModel/Messages.Designer.cs b/XenModel/Messages.Designer.cs index 2d47ae38ce..c9719a457a 100755 --- a/XenModel/Messages.Designer.cs +++ b/XenModel/Messages.Designer.cs @@ -7948,9 +7948,9 @@ public static string CHECKING_FOR_PENDING_RESTART { /// /// Looks up a localized string similar to Checking HA and WLB status. /// - public static string CHECKING_HA_STATUS { + public static string CHECKING_HA_AND_WLB_STATUS { get { - return ResourceManager.GetString("CHECKING_HA_STATUS", resourceCulture); + return ResourceManager.GetString("CHECKING_HA_AND_WLB_STATUS", resourceCulture); } } @@ -18835,6 +18835,15 @@ public static string HA_CANNOT_EVACUATE_COORDINATOR { } } + /// + /// Looks up a localized string similar to HA check. + /// + public static string HA_CHECK_DESCRIPTION { + get { + return ResourceManager.GetString("HA_CHECK_DESCRIPTION", resourceCulture); + } + } + /// /// Looks up a localized string similar to Choose a heartbeat SR. /// @@ -19498,15 +19507,6 @@ public static string HA_WIZARD_FINISH_PAGE_TITLE { } } - /// - /// Looks up a localized string similar to HA and WLB check. - /// - public static string HA_WLB_CHECK_DESCRIPTION { - get { - return ResourceManager.GetString("HA_WLB_CHECK_DESCRIPTION", resourceCulture); - } - } - /// /// Looks up a localized string similar to Has any custom field. /// @@ -38036,6 +38036,15 @@ public static string UPDATES_WIZARD_FILE_NOT_FOUND { } } + /// + /// Looks up a localized string similar to {0}: Check skipped because HA and WLB are enabled on pool {1}.. + /// + public static string UPDATES_WIZARD_HA_AND_WLB_ON_WARNING { + get { + return ResourceManager.GetString("UPDATES_WIZARD_HA_AND_WLB_ON_WARNING", resourceCulture); + } + } + /// /// Looks up a localized string similar to Pool '{0}' cannot have HA enabled.. /// @@ -41499,6 +41508,15 @@ public static string WLB_AUTOMATION_SUBTEXT { } } + /// + /// Looks up a localized string similar to WLB check. + /// + public static string WLB_CHECK_DESCRIPTION { + get { + return ResourceManager.GetString("WLB_CHECK_DESCRIPTION", resourceCulture); + } + } + /// /// Looks up a localized string similar to Workload &Balancing. /// diff --git a/XenModel/Messages.resx b/XenModel/Messages.resx index 2e4f0691d1..4a08d6a28e 100755 --- a/XenModel/Messages.resx +++ b/XenModel/Messages.resx @@ -2866,7 +2866,7 @@ Do you want to assign these VMs to the schedule '{0}' instead? Checking for pending restart - + Checking HA and WLB status @@ -6580,6 +6580,9 @@ not currently live: Server '{0}' cannot be placed in Maintenance Mode because it is the coordinator of an HA-enabled pool. + + HA check + Choose a heartbeat SR @@ -6817,9 +6820,6 @@ Click Configure HA to alter the settings displayed below. Review configuration and activate HA - - HA and WLB check - Has any custom field @@ -13136,6 +13136,9 @@ Note that if RBAC is enabled, only updates which you have privileges to dismiss Check your settings and try again. + + {0}: Check skipped because HA and WLB are enabled on pool {1}. + Pool '{0}' cannot have HA enabled. @@ -14301,6 +14304,9 @@ Schedule: Configure WLB Automation + + WLB check + Workload &Balancing From 9a80dc92259bab800149de0822b498ef350992d0 Mon Sep 17 00:00:00 2001 From: Chris <86611314+CitrixChris@users.noreply.github.com> Date: Mon, 30 Oct 2023 13:40:18 +0000 Subject: [PATCH 4/4] CP-40844: Adds download source action that gets the latest source code in the production stage (#3153) * CP-40844 adds download source action that gets the latest source code in the production stage. Signed-off-by: Chris Lancaster CP-40844 refactors download file actions Signed-off-by: Chris Lancaster Adds source url to branding.sh Signed-off-by: Chris Lancaster CP40844 refactors code and implements reviewers comments, aswell as better message handeling Signed-off-by: Chris Lancaster CP-40844 Sorts messages Signed-off-by: Chris Lancaster CP-40844 removes stray console logging Signed-off-by: Chris Lancaster CP-40844 Changes to use FirstOrDefault to avoid null exceptions/errors Signed-off-by: Chris Lancaster CP-40844 removes unessessary usings Signed-off-by: Chris Lancaster CP-40844 code tidy up Signed-off-by: Chris Lancaster CP-40844 Removes erroneous root tag from merge conflict resolution Signed-off-by: Chris Lancaster CP-40844 sorts messages Signed-off-by: Chris Lancaster * CP-40844 fixes misnamed method and adds sourceurl parameter to brandmanager Signed-off-by: Chris Lancaster * CP-40844 Source url now uses XCUpdates url and just substitues the name of the file Signed-off-by: Chris Lancaster * CP-40844 Adds source url to xenadmin-build.ps1 Signed-off-by: Chris Lancaster * CP-40844 defines log4net in each class not just inherited Signed-off-by: Chris Lancaster * CP-40844 removes unused class variable Signed-off-by: Chris Lancaster * CP-40844 removes unneeded class variable and uses parent property instead. Simplifies messages for source download and client update. Signed-off-by: Chris Lancaster * CP-40844 reduces public exposure of member variables/properties Signed-off-by: Chris Lancaster * CP-40844 merges download x file actions into single file. Signed-off-by: Chris Lancaster * CP-40844 overide ReleaseDownloadedContent in DownloadAndUpdateClientAction to handle disposal of FileStream Signed-off-by: Chris Lancaster * CP-40844 minor fixes/tidy up Signed-off-by: Chris Lancaster * CP-40844 defaults the download latest source button to be invisible. Signed-off-by: Chris Lancaster * CP-40844 if there is no client update detected then we shouldnt show a where to save dialog Signed-off-by: Chris Lancaster * CP-40844 Directs user to xenserver website to download source if automatic update checks are turned off. Renames message OUT_OF_DATE_WEBSITE to WEBSITE_DOWNLOADS Signed-off-by: Chris Lancaster * CP-40844 updates source param to sourceUrl in XCUpdates.xml Signed-off-by: Chris Lancaster * CP-40844 moves strings to Messages and applys source name string to relevant places Signed-off-by: Chris Lancaster * CP-40844 removes sourceurl from brand manager Signed-off-by: Chris Lancaster * CP-40844 uses string literal rather than string join Signed-off-by: Chris Lancaster * Some more corrections. Signed-off-by: Konstantina Chremmou --------- Signed-off-by: Chris Lancaster Signed-off-by: Konstantina Chremmou Co-authored-by: Konstantina Chremmou --- XenAdmin/Alerts/Types/ClientUpdateAlert.cs | 49 ++++- XenAdmin/Alerts/Types/GuiOldAlert.cs | 2 +- XenAdmin/Core/Updates.cs | 2 +- XenAdmin/MainWindow.Designer.cs | 20 +- XenAdmin/MainWindow.cs | 23 ++- XenAdmin/MainWindow.resx | 25 ++- .../AlertTests/XenCenterUpdateAlertTests.cs | 4 +- XenModel/Actions/Updates/ClientVersion.cs | 4 +- ...eClientAction.cs => DownloadFileAction.cs} | 192 +++++++++++------- .../Updates/DownloadUpdatesXmlAction.cs | 5 +- XenModel/InvisibleMessages.Designer.cs | 18 +- XenModel/InvisibleMessages.resx | 6 +- XenModel/Messages.Designer.cs | 63 +++++- XenModel/Messages.resx | 21 +- XenModel/XenModel.csproj | 2 +- scripts/xenadmin-build.ps1 | 8 +- 16 files changed, 327 insertions(+), 117 deletions(-) rename XenModel/Actions/Updates/{DownloadAndUpdateClientAction.cs => DownloadFileAction.cs} (64%) diff --git a/XenAdmin/Alerts/Types/ClientUpdateAlert.cs b/XenAdmin/Alerts/Types/ClientUpdateAlert.cs index 233d193240..b6ee5ab921 100644 --- a/XenAdmin/Alerts/Types/ClientUpdateAlert.cs +++ b/XenAdmin/Alerts/Types/ClientUpdateAlert.cs @@ -31,13 +31,15 @@ using System; using System.Diagnostics; using System.IO; +using System.Linq; using System.Windows.Forms; using XenAdmin.Actions; using XenAdmin.Actions.GUIActions; using XenAdmin.Actions.Updates; using XenAdmin.Core; using XenAdmin.Dialogs; - +using XenAdmin.Dialogs.WarningDialogs; +using XenCenterLib; namespace XenAdmin.Alerts { @@ -123,10 +125,13 @@ public static void DownloadAndInstallNewClient(ClientUpdateAlert updateAlert, IW if (currentTasks) { - if (new Dialogs.WarningDialogs.CloseXenCenterWarningDialog(true).ShowDialog(parent) != DialogResult.OK) + using (var dlg = new CloseXenCenterWarningDialog(true)) { - downloadAndInstallClientAction.ReleaseInstaller(); - return; + if (dlg.ShowDialog(parent) != DialogResult.OK) + { + downloadAndInstallClientAction.ReleaseDownloadedContent(); + return; + } } } @@ -134,17 +139,49 @@ public static void DownloadAndInstallNewClient(ClientUpdateAlert updateAlert, IW { Process.Start(outputPathAndFileName); log.DebugFormat("Update {0} found and install started", updateAlert.Name); - downloadAndInstallClientAction.ReleaseInstaller(); + downloadAndInstallClientAction.ReleaseDownloadedContent(); Application.Exit(); } catch (Exception e) { log.Error("Exception occurred when starting the installation process.", e); - downloadAndInstallClientAction.ReleaseInstaller(true); + downloadAndInstallClientAction.ReleaseDownloadedContent(true); using (var dlg = new ErrorDialog(Messages.UPDATE_CLIENT_FAILED_INSTALLER_LAUNCH)) dlg.ShowDialog(parent); } } + + public static void DownloadSource(IWin32Window parent) + { + // If no update no need to show where to save dialog + var clientVersion = Updates.ClientVersions.FirstOrDefault(); + + if (clientVersion == null) + { + // There is no XCUpdates.xml so direct to website. + Program.OpenURL(InvisibleMessages.WEBSITE_DOWNLOADS); + } + else + { + string outputPathAndFileName; + using (var saveSourceDialog = new SaveFileDialog()) + { + saveSourceDialog.FileName = $"{BrandManager.BrandConsole}-v{clientVersion.Version}-source.zip"; + saveSourceDialog.DefaultExt = "zip"; + saveSourceDialog.Filter = "(*.zip)|*.zip|All files (*.*)|*.*"; + saveSourceDialog.InitialDirectory = Win32.GetKnownFolderPath(Win32.KnownFolders.Downloads); + + if (saveSourceDialog.ShowDialog(parent) != DialogResult.OK) + { + return; + } + outputPathAndFileName = saveSourceDialog.FileName; + } + + var downloadSourceAction = new DownloadSourceAction(clientVersion.Name, new Uri(clientVersion.SourceUrl), outputPathAndFileName); + downloadSourceAction.RunAsync(); + } + } } } diff --git a/XenAdmin/Alerts/Types/GuiOldAlert.cs b/XenAdmin/Alerts/Types/GuiOldAlert.cs index 62db440330..ad0cb53e1f 100644 --- a/XenAdmin/Alerts/Types/GuiOldAlert.cs +++ b/XenAdmin/Alerts/Types/GuiOldAlert.cs @@ -49,7 +49,7 @@ public GuiOldAlert() public override Action FixLinkAction { - get { return () => Program.OpenURL(InvisibleMessages.OUT_OF_DATE_WEBSITE); } + get { return () => Program.OpenURL(InvisibleMessages.WEBSITE_DOWNLOADS); } } public override string FixLinkText => Messages.ALERT_NEW_VERSION_DOWNLOAD; diff --git a/XenAdmin/Core/Updates.cs b/XenAdmin/Core/Updates.cs index 0ee6cd53dd..bebe4c2931 100644 --- a/XenAdmin/Core/Updates.cs +++ b/XenAdmin/Core/Updates.cs @@ -67,7 +67,7 @@ public class Updates private static List XenServerVersionsForAutoCheck = new List(); private static List XenServerPatches = new List(); - private static List ClientVersions = new List(); + public static List ClientVersions = new List(); public static List XenServerVersions = new List(); private static readonly List updateAlerts = new List(); diff --git a/XenAdmin/MainWindow.Designer.cs b/XenAdmin/MainWindow.Designer.cs index 2d22626118..f4e7382e2b 100644 --- a/XenAdmin/MainWindow.Designer.cs +++ b/XenAdmin/MainWindow.Designer.cs @@ -279,11 +279,13 @@ private void InitializeComponent() this.xenCenterPluginsOnlineToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator(); this.pluginItemsPlaceHolderToolStripMenuItem8 = new System.Windows.Forms.ToolStripMenuItem(); + this.downloadLatestSourceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutXenSourceAdminToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainMenuBar = new XenAdmin.Controls.MenuStripEx(); this.updateClientToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.downloadInstallToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.relNotesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.downloadSourceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.securityGroupsToolStripMenuItem = new XenAdmin.Commands.CommandToolStripMenuItem(); this.MenuPanel = new System.Windows.Forms.Panel(); this.StatusStrip = new XenAdmin.Controls.StatusStripEx(); @@ -1867,6 +1869,7 @@ private void InitializeComponent() this.xenCenterPluginsOnlineToolStripMenuItem, this.toolStripSeparator7, this.pluginItemsPlaceHolderToolStripMenuItem8, + this.downloadLatestSourceToolStripMenuItem, this.aboutXenSourceAdminToolStripMenuItem}); this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; resources.ApplyResources(this.helpToolStripMenuItem, "helpToolStripMenuItem"); @@ -1928,6 +1931,12 @@ private void InitializeComponent() this.pluginItemsPlaceHolderToolStripMenuItem8.Name = "pluginItemsPlaceHolderToolStripMenuItem8"; resources.ApplyResources(this.pluginItemsPlaceHolderToolStripMenuItem8, "pluginItemsPlaceHolderToolStripMenuItem8"); // + // downloadLatestSourceToolStripMenuItem + // + this.downloadLatestSourceToolStripMenuItem.Name = "downloadLatestSourceToolStripMenuItem"; + resources.ApplyResources(this.downloadLatestSourceToolStripMenuItem, "downloadLatestSourceToolStripMenuItem"); + this.downloadLatestSourceToolStripMenuItem.Click += new System.EventHandler(this.downloadLatestSourceToolStripMenuItem_Click); + // // aboutXenSourceAdminToolStripMenuItem // this.aboutXenSourceAdminToolStripMenuItem.Name = "aboutXenSourceAdminToolStripMenuItem"; @@ -1960,7 +1969,8 @@ private void InitializeComponent() this.updateClientToolStripMenuItem.BackColor = System.Drawing.SystemColors.Control; this.updateClientToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.downloadInstallToolStripMenuItem, - this.relNotesToolStripMenuItem}); + this.relNotesToolStripMenuItem, + this.downloadSourceToolStripMenuItem}); this.updateClientToolStripMenuItem.Image = global::XenAdmin.Properties.Resources._015_Download_h32bit_16; this.updateClientToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 5, 0); this.updateClientToolStripMenuItem.Name = "updateClientToolStripMenuItem"; @@ -1978,6 +1988,12 @@ private void InitializeComponent() resources.ApplyResources(this.relNotesToolStripMenuItem, "relNotesToolStripMenuItem"); this.relNotesToolStripMenuItem.Click += new System.EventHandler(this.relNotesToolStripMenuItem_Click); // + // downloadSourceToolStripMenuItem + // + this.downloadSourceToolStripMenuItem.Name = "downloadSourceToolStripMenuItem"; + resources.ApplyResources(this.downloadSourceToolStripMenuItem, "downloadSourceToolStripMenuItem"); + this.downloadSourceToolStripMenuItem.Click += new System.EventHandler(this.downloadSourceToolStripMenuItem_Click); + // // securityGroupsToolStripMenuItem // this.securityGroupsToolStripMenuItem.Name = "securityGroupsToolStripMenuItem"; @@ -2344,6 +2360,8 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem toolStripMenuItemCfu; private System.Windows.Forms.ToolStripSeparator toolStripSeparator32; private XenAdmin.Commands.CommandToolStripMenuItem toolStripMenuItemVtpm; + private System.Windows.Forms.ToolStripMenuItem downloadSourceToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem downloadLatestSourceToolStripMenuItem; private System.Windows.Forms.Label labelFiltersOnOff; private System.Windows.Forms.ToolStripMenuItem configureUpdatesToolStripMenuItem; private TabPages.ManageCdnUpdatesPage cdnUpdatesPage; diff --git a/XenAdmin/MainWindow.cs b/XenAdmin/MainWindow.cs index 14e3f6c180..a9da97ca9c 100755 --- a/XenAdmin/MainWindow.cs +++ b/XenAdmin/MainWindow.cs @@ -62,6 +62,7 @@ using XenAdmin.Controls.GradientPanel; using XenAdmin.Dialogs.ServerUpdates; using XenAdmin.Help; +using XenAdmin.Actions.Updates; namespace XenAdmin { @@ -269,6 +270,7 @@ public MainWindow(string[] args) statusButtonAlerts.Visible = statusButtonUpdates.Visible = statusButtonCdnUpdates.Visible = statusButtonProgress.Visible = statusButtonErrors.Visible = false; statusButtonUpdates.ToolTipText = string.Format(statusButtonUpdates.ToolTipText, BrandManager.ProductVersion821); statusButtonCdnUpdates.ToolTipText = string.Format(statusButtonCdnUpdates.ToolTipText, BrandManager.ProductBrand, BrandManager.ProductVersionPost82); + downloadLatestSourceToolStripMenuItem.Text = Messages.DOWNLOAD_LATEST_SOURCE; } private void RegisterEvents() @@ -966,7 +968,7 @@ private void connection_CachePopulated(IXenConnection connection) Program.Invoke(Program.MainWindow, delegate { var msg = string.Format(Messages.GUI_OUT_OF_DATE, BrandManager.BrandConsole, Helpers.GetName(coordinator)); - var url = InvisibleMessages.OUT_OF_DATE_WEBSITE; + var url = InvisibleMessages.WEBSITE_DOWNLOADS; var title = string.Format(Messages.CONNECTION_REFUSED_TITLE, Helpers.GetName(coordinator).Ellipsise(80)); var error = $"{msg}\n{url}"; @@ -994,7 +996,7 @@ private void connection_CachePopulated(IXenConnection connection) { var msg = string.Format(Messages.GUI_NOT_COMPATIBLE, BrandManager.BrandConsole, BrandManager.ProductVersion712, BrandManager.ProductVersion80, Helpers.GetName(coordinator)); - var url = InvisibleMessages.OUT_OF_DATE_WEBSITE; + var url = InvisibleMessages.WEBSITE_DOWNLOADS; var title = string.Format(Messages.CONNECTION_REFUSED_TITLE, Helpers.GetName(coordinator).Ellipsise(80)); var error = $"{msg}\n{url}"; @@ -2715,7 +2717,14 @@ private void SetClientUpdateAlert() { updateAlert = Updates.ClientUpdateAlerts.FirstOrDefault(); if (updateAlert != null) + { relNotesToolStripMenuItem.Text = string.Format(Messages.MAINWINDOW_UPDATE_RELEASE, updateAlert.NewVersion.Version); + downloadSourceToolStripMenuItem.Text = string.Format(Messages.DOWNLOAD_SOURCE, BrandManager.BrandConsole, updateAlert.NewVersion.Version); + } + var clientVersion = Updates.ClientVersions.FirstOrDefault(); + downloadLatestSourceToolStripMenuItem.Text = clientVersion != null + ? string.Format(Messages.DOWNLOAD_SOURCE, BrandManager.BrandConsole, clientVersion.Version) + : string.Format(Messages.DOWNLOAD_LATEST_SOURCE, BrandManager.BrandConsole); updateClientToolStripMenuItem.Visible = updateAlert != null; } @@ -3391,5 +3400,15 @@ private void configureUpdatesToolStripMenuItem_Click(object sender, EventArgs e) using (var dialog = new ConfigUpdatesDialog()) dialog.ShowDialog(this); } + + private void downloadSourceToolStripMenuItem_Click(object sender, EventArgs e) + { + ClientUpdateAlert.DownloadSource(this); + } + + private void downloadLatestSourceToolStripMenuItem_Click(object sender, EventArgs e) + { + ClientUpdateAlert.DownloadSource(this); + } } } diff --git a/XenAdmin/MainWindow.resx b/XenAdmin/MainWindow.resx index e915993871..e9b37af946 100644 --- a/XenAdmin/MainWindow.resx +++ b/XenAdmin/MainWindow.resx @@ -2772,6 +2772,9 @@ PluginItemsPlaceHolder + + 186, 22 + 186, 22 @@ -2797,17 +2800,23 @@ Microsoft Sans Serif, 8.25pt - 173, 22 + 180, 22 Download and &Install - 173, 22 + 180, 22 v{0} &Release Notes + + 180, 22 + + + Download v{0} source + 87, 20 @@ -4143,6 +4152,12 @@ System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + downloadLatestSourceToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + aboutXenSourceAdminToolStripMenuItem @@ -4167,6 +4182,12 @@ System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + downloadSourceToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + securityGroupsToolStripMenuItem diff --git a/XenAdminTests/UnitTests/AlertTests/XenCenterUpdateAlertTests.cs b/XenAdminTests/UnitTests/AlertTests/XenCenterUpdateAlertTests.cs index 0d919a3c2d..146c23af4b 100644 --- a/XenAdminTests/UnitTests/AlertTests/XenCenterUpdateAlertTests.cs +++ b/XenAdminTests/UnitTests/AlertTests/XenCenterUpdateAlertTests.cs @@ -43,8 +43,8 @@ public class XenCenterUpdateAlertTests public void VerifyStoredDataWithDefaultConstructor() { var version = new ClientVersion("6.0.2", "xc", true, false, "http://url", - new DateTime(2011, 12, 09).ToString(), "abcde"); - + new DateTime(2011, 12, 09).ToString(), "abcde", "http://sourceurl"); + ClassVerifiers.VerifyGetters(new ClientUpdateAlert(version), new UpdateAlertClassUnitTestData { diff --git a/XenModel/Actions/Updates/ClientVersion.cs b/XenModel/Actions/Updates/ClientVersion.cs index 2d3a15034f..831c00700e 100644 --- a/XenModel/Actions/Updates/ClientVersion.cs +++ b/XenModel/Actions/Updates/ClientVersion.cs @@ -43,8 +43,9 @@ public class ClientVersion public string Lang; public DateTime TimeStamp; public string Checksum; + public string SourceUrl; - public ClientVersion(string version_lang, string name, bool latest, bool latest_cr, string url, string timestamp, string checksum) + public ClientVersion(string version_lang, string name, bool latest, bool latest_cr, string url, string timestamp, string checksum, string sourceUrl) { ParseVersion(version_lang); Name = name; @@ -53,6 +54,7 @@ public ClientVersion(string version_lang, string name, bool latest, bool latest_ Url = url; DateTime.TryParse(timestamp, out TimeStamp); Checksum = checksum; + SourceUrl = sourceUrl; } private void ParseVersion(string version_lang) diff --git a/XenModel/Actions/Updates/DownloadAndUpdateClientAction.cs b/XenModel/Actions/Updates/DownloadFileAction.cs similarity index 64% rename from XenModel/Actions/Updates/DownloadAndUpdateClientAction.cs rename to XenModel/Actions/Updates/DownloadFileAction.cs index 9849350b84..e8271fca7d 100644 --- a/XenModel/Actions/Updates/DownloadAndUpdateClientAction.cs +++ b/XenModel/Actions/Updates/DownloadFileAction.cs @@ -42,38 +42,36 @@ namespace XenAdmin.Actions.Updates { - public class DownloadAndUpdateClientAction : AsyncAction, IByteProgressAction + public class DownloadFileAction : AsyncAction, IByteProgressAction { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private const int SLEEP_TIME_TO_CHECK_DOWNLOAD_STATUS_MS = 900; private const int SLEEP_TIME_BEFORE_RETRY_MS = 5000; - //If you consider increasing this for any reason (I think 5 is already more than enough), have a look at the usage of SLEEP_TIME_BEFORE_RETRY_MS in DownloadFile() as well. + //If you consider increasing this for any reason (I think 5 is already more than enough), + //have a look at the usage of SLEEP_TIME_BEFORE_RETRY_MS in DownloadFile() as well. private const int MAX_NUMBER_OF_TRIES = 5; private readonly Uri _address; private readonly string _outputPathAndFileName; - private readonly string _updateName; - private readonly bool _downloadUpdate; - private DownloadState _updateDownloadState; - private Exception _updateDownloadError; - private readonly string _checksum; + private readonly string _fileName; + private readonly bool _canDownloadFile; + private DownloadState _fileState; + private Exception _downloadError; private readonly string _authToken; private WebClient _client; - private FileStream _msiStream; + protected string OutputPathAndFileName => _outputPathAndFileName; public string ByteProgressDescription { get; set; } - public DownloadAndUpdateClientAction(string updateName, Uri uri, string outputFileName, string checksum) - : base(null, string.Format(Messages.DOWNLOAD_CLIENT_INSTALLER_ACTION_TITLE, updateName), - string.Empty, true) + protected DownloadFileAction(string fileName, Uri uri, string outputFileName, string title, bool suppressHistory) + : base(null, title, fileName, suppressHistory) { - _updateName = updateName; + _fileName = fileName; _address = uri; - _downloadUpdate = _address != null; + _canDownloadFile = _address != null; _outputPathAndFileName = outputFileName; - _checksum = checksum; _authToken = XenAdminConfigManager.Provider.GetClientUpdatesQueryParam(); } @@ -105,7 +103,7 @@ private void DownloadFile() _client.Proxy = XenAdminConfigManager.Provider.GetProxyFromSettings(null, false); //start the download - _updateDownloadState = DownloadState.InProgress; + _fileState = DownloadState.InProgress; var uriBuilder = new UriBuilder(_address); @@ -122,7 +120,7 @@ private void DownloadFile() bool updateDownloadCancelling = false; //wait for the file to be downloaded - while (_updateDownloadState == DownloadState.InProgress) + while (_fileState == DownloadState.InProgress) { if (!updateDownloadCancelling && (Cancelling || Cancelled)) { @@ -134,10 +132,10 @@ private void DownloadFile() Thread.Sleep(SLEEP_TIME_TO_CHECK_DOWNLOAD_STATUS_MS); } - if (_updateDownloadState == DownloadState.Cancelled) + if (_fileState == DownloadState.Cancelled) throw new CancelledException(); - if (_updateDownloadState == DownloadState.Error) + if (_fileState == DownloadState.Error) { needToRetry = true; @@ -149,10 +147,10 @@ private void DownloadFile() "Error while downloading from '{0}'. Number of errors so far (including this): {1}. Trying maximum {2} times.", _address, errorCount, MAX_NUMBER_OF_TRIES); - if (_updateDownloadError == null) + if (_downloadError == null) log.Error("An unknown error occurred."); else - log.Error(_updateDownloadError); + log.Error(_downloadError); } } while (errorCount < MAX_NUMBER_OF_TRIES && needToRetry); } @@ -167,30 +165,29 @@ private void DownloadFile() } //if this is still the case after having retried MAX_NUMBER_OF_TRIES number of times. - if (_updateDownloadState == DownloadState.Error) + if (_fileState == DownloadState.Error) { log.ErrorFormat("Giving up - Maximum number of retries ({0}) has been reached.", MAX_NUMBER_OF_TRIES); - throw _updateDownloadError ?? new Exception(Messages.ERROR_UNKNOWN); + throw _downloadError ?? new Exception(Messages.ERROR_UNKNOWN); } } private void NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) { - if (!e.IsAvailable && _client != null && _updateDownloadState == DownloadState.InProgress) + if (!e.IsAvailable && _client != null && _fileState == DownloadState.InProgress) { - _updateDownloadError = new WebException(Messages.NETWORK_CONNECTIVITY_ERROR); - _updateDownloadState = DownloadState.Error; + _downloadError = new WebException(Messages.NETWORK_CONNECTIVITY_ERROR); + _fileState = DownloadState.Error; _client.CancelAsync(); } } protected override void Run() { - if (!_downloadUpdate) + if (!_canDownloadFile) return; - log.InfoFormat("Downloading '{0}' installer (from '{1}') to '{2}'", _updateName, _address, _outputPathAndFileName); - Description = string.Format(Messages.DOWNLOAD_CLIENT_INSTALLER_ACTION_DESCRIPTION, _updateName); + log.InfoFormat("Downloading '{0}' (from '{1}') to '{2}'", _fileName, _address, _outputPathAndFileName); LogDescriptionChanges = false; DownloadFile(); LogDescriptionChanges = true; @@ -202,23 +199,30 @@ protected override void Run() throw new CancelledException(); if (!File.Exists(_outputPathAndFileName)) - throw new Exception(Messages.DOWNLOAD_CLIENT_INSTALLER_MSI_NOT_FOUND); + throw new Exception(GetDownloadedFileNotFoundMessage()); - ValidateMsi(); + ValidateDownloadedFile(); Description = Messages.COMPLETED; } protected override void CleanOnError() { - ReleaseInstaller(true); + ReleaseDownloadedContent(true); + } + + protected virtual string GetDownloadedFileNotFoundMessage() + { + return Messages.DOWNLOAD_FILE_ACTION_NOT_FOUND; } - public void ReleaseInstaller(bool deleteMsi = false) + protected virtual void ValidateDownloadedFile() { - _msiStream?.Dispose(); + } - if (!deleteMsi) + public virtual void ReleaseDownloadedContent(bool deleteDownloadedContent = false) + { + if (!deleteDownloadedContent) return; try @@ -232,27 +236,89 @@ public void ReleaseInstaller(bool deleteMsi = false) } } - private void ValidateMsi() + private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) + { + int pc = (int)(95.0 * e.BytesReceived / e.TotalBytesToReceive); + + var descr = string.Format(Messages.DOWNLOAD_FILE_ACTION_PROGRESS_DESCRIPTION, _fileName, + Util.DiskSizeString(e.BytesReceived, "F1"), + Util.DiskSizeString(e.TotalBytesToReceive)); + ByteProgressDescription = descr; + Tick(pc, descr); + } + + private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) + { + if (e.Cancelled && _fileState == DownloadState.Error) // cancelled due to network connectivity issue (see NetworkAvailabilityChanged) + return; + + if (e.Cancelled) + { + _fileState = DownloadState.Cancelled; + log.DebugFormat("'{0}' download cancelled by the user", _fileName); + return; + } + + if (e.Error != null) + { + _downloadError = e.Error; + log.DebugFormat("'{0}' download failed", _fileName); + _fileState = DownloadState.Error; + return; + } + + _fileState = DownloadState.Completed; + log.DebugFormat("'{0}' download completed successfully", _fileName); + } + + public override void RecomputeCanCancel() + { + CanCancel = !Cancelling && !IsCompleted && _fileState == DownloadState.InProgress; + } + } + + public class DownloadAndUpdateClientAction : DownloadFileAction + { + private readonly string _checksum; + private FileStream _msiStream; + + public DownloadAndUpdateClientAction(string installerName, Uri uri, string outputFileName, string checksum) + : base(installerName, + uri, + outputFileName, + string.Format(Messages.DOWNLOAD_CLIENT_INSTALLER_ACTION_TITLE, installerName), + true) + { + _checksum = checksum; + Description = string.Format(Messages.DOWNLOAD_CLIENT_INSTALLER_ACTION_DESCRIPTION, installerName); + } + + protected override string GetDownloadedFileNotFoundMessage() + { + return Messages.DOWNLOAD_CLIENT_INSTALLER_MSI_NOT_FOUND; + } + + protected override void ValidateDownloadedFile() { Description = Messages.UPDATE_CLIENT_VALIDATING_INSTALLER; - _msiStream = new FileStream(_outputPathAndFileName, FileMode.Open, FileAccess.Read); + _msiStream = new FileStream(OutputPathAndFileName, FileMode.Open, FileAccess.Read); - var calculatedChecksum = string.Empty; + var calculatedChecksum = string.Empty; var hash = StreamUtilities.ComputeHash(_msiStream, out _); if (hash != null) calculatedChecksum = string.Join(string.Empty, hash.Select(b => $"{b:x2}")); - // Check if calculatedChecksum matches what is in chcupdates.xml + // Check if calculatedChecksum matches what is in xcupdates.xml if (!_checksum.Equals(calculatedChecksum, StringComparison.InvariantCultureIgnoreCase)) - throw new Exception(Messages.UPDATE_CLIENT_INVALID_CHECKSUM ); + throw new Exception(Messages.UPDATE_CLIENT_INVALID_CHECKSUM); bool valid; try { // Check digital signature of .msi - using (var basicSigner = X509Certificate.CreateFromSignedFile(_outputPathAndFileName)) + using (var basicSigner = X509Certificate.CreateFromSignedFile(OutputPathAndFileName)) { using (var cert = new X509Certificate2(basicSigner)) valid = cert.Verify(); @@ -267,43 +333,23 @@ private void ValidateMsi() throw new Exception(Messages.UPDATE_CLIENT_INVALID_DIGITAL_CERTIFICATE); } - private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) + public override void ReleaseDownloadedContent(bool deleteDownloadedContent = false) { - int pc = (int)(95.0 * e.BytesReceived / e.TotalBytesToReceive); - var descr = string.Format(Messages.DOWNLOAD_CLIENT_INSTALLER_ACTION_PROGRESS_DESCRIPTION, _updateName, - Util.DiskSizeString(e.BytesReceived, "F1"), - Util.DiskSizeString(e.TotalBytesToReceive)); - ByteProgressDescription = descr; - Tick(pc, descr); - } - - private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) - { - if (e.Cancelled && _updateDownloadState == DownloadState.Error) // cancelled due to network connectivity issue (see NetworkAvailabilityChanged) - return; - - if (e.Cancelled) - { - _updateDownloadState = DownloadState.Cancelled; - log.DebugFormat("Client update '{0}' download cancelled by the user", _updateName); - return; - } - - if (e.Error != null) - { - _updateDownloadError = e.Error; - log.DebugFormat("Client update '{0}' download failed", _updateName); - _updateDownloadState = DownloadState.Error; - return; - } - - _updateDownloadState = DownloadState.Completed; - log.DebugFormat("Client update '{0}' download completed successfully", _updateName); + _msiStream.Dispose(); + base.ReleaseDownloadedContent(deleteDownloadedContent); } + } - public override void RecomputeCanCancel() + public class DownloadSourceAction : DownloadFileAction + { + public DownloadSourceAction(string sourceName, Uri uri, string outputFileName) + : base(sourceName, + uri, + outputFileName, + string.Format(Messages.DOWNLOAD_CLIENT_SOURCE_ACTION_TITLE, sourceName), + false) { - CanCancel = !Cancelling && !IsCompleted && (_updateDownloadState == DownloadState.InProgress); + Description = string.Format(Messages.DOWNLOAD_CLIENT_SOURCE_ACTION_DESCRIPTION, sourceName); } } } diff --git a/XenModel/Actions/Updates/DownloadUpdatesXmlAction.cs b/XenModel/Actions/Updates/DownloadUpdatesXmlAction.cs index 39c4fd3cca..39ea0db635 100644 --- a/XenModel/Actions/Updates/DownloadUpdatesXmlAction.cs +++ b/XenModel/Actions/Updates/DownloadUpdatesXmlAction.cs @@ -105,6 +105,7 @@ private void GetXenCenterVersions(XmlDocument xdoc) bool latest = false; bool latestCr = false; string url = string.Empty; + string sourceUrl = string.Empty; string timestamp = string.Empty; string checksum = string.Empty; @@ -120,13 +121,15 @@ private void GetXenCenterVersions(XmlDocument xdoc) latestCr = attrib.Value.ToUpperInvariant() == bool.TrueString.ToUpperInvariant(); else if (attrib.Name == "url") url = attrib.Value; + else if (attrib.Name == "sourceUrl") + sourceUrl = attrib.Value; else if (attrib.Name == "timestamp") timestamp = attrib.Value; else if (attrib.Name == "checksum") checksum = attrib.Value; } - ClientVersions.Add(new ClientVersion(versionLang, name, latest, latestCr, url, timestamp, checksum)); + ClientVersions.Add(new ClientVersion(versionLang, name, latest, latestCr, url, timestamp, checksum, sourceUrl)); } } } diff --git a/XenModel/InvisibleMessages.Designer.cs b/XenModel/InvisibleMessages.Designer.cs index 56caace06a..89188031cb 100644 --- a/XenModel/InvisibleMessages.Designer.cs +++ b/XenModel/InvisibleMessages.Designer.cs @@ -150,15 +150,6 @@ public static string LOCALE { } } - /// - /// Looks up a localized string similar to https://www.xenserver.com/downloads. - /// - public static string OUT_OF_DATE_WEBSITE { - get { - return ResourceManager.GetString("OUT_OF_DATE_WEBSITE", resourceCulture); - } - } - /// /// Looks up a localized string similar to https://github.com/xenserver/xencenter-samples. /// @@ -212,5 +203,14 @@ public static string UPSELL_LEARNMOREURL_TRIAL { return ResourceManager.GetString("UPSELL_LEARNMOREURL_TRIAL", resourceCulture); } } + + /// + /// Looks up a localized string similar to https://www.xenserver.com/downloads. + /// + public static string WEBSITE_DOWNLOADS { + get { + return ResourceManager.GetString("WEBSITE_DOWNLOADS", resourceCulture); + } + } } } diff --git a/XenModel/InvisibleMessages.resx b/XenModel/InvisibleMessages.resx index 3ff214dac4..7c1bdf9f2a 100644 --- a/XenModel/InvisibleMessages.resx +++ b/XenModel/InvisibleMessages.resx @@ -147,9 +147,6 @@ en-US - - https://www.xenserver.com/downloads - https://github.com/xenserver/xencenter-samples @@ -168,4 +165,7 @@ https://www.xenserver.com/ + + https://www.xenserver.com/downloads + diff --git a/XenModel/Messages.Designer.cs b/XenModel/Messages.Designer.cs index c9719a457a..bf9759448b 100755 --- a/XenModel/Messages.Designer.cs +++ b/XenModel/Messages.Designer.cs @@ -14058,15 +14058,6 @@ public static string DOWNLOAD_CLIENT_INSTALLER_ACTION_DESCRIPTION { } } - /// - /// Looks up a localized string similar to Downloading {0} installer ({1} of {2}).... - /// - public static string DOWNLOAD_CLIENT_INSTALLER_ACTION_PROGRESS_DESCRIPTION { - get { - return ResourceManager.GetString("DOWNLOAD_CLIENT_INSTALLER_ACTION_PROGRESS_DESCRIPTION", resourceCulture); - } - } - /// /// Looks up a localized string similar to Download {0} installer. /// @@ -14085,6 +14076,24 @@ public static string DOWNLOAD_CLIENT_INSTALLER_MSI_NOT_FOUND { } } + /// + /// Looks up a localized string similar to Downloading {0} source code. + /// + public static string DOWNLOAD_CLIENT_SOURCE_ACTION_DESCRIPTION { + get { + return ResourceManager.GetString("DOWNLOAD_CLIENT_SOURCE_ACTION_DESCRIPTION", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download {0} source code. + /// + public static string DOWNLOAD_CLIENT_SOURCE_ACTION_TITLE { + get { + return ResourceManager.GetString("DOWNLOAD_CLIENT_SOURCE_ACTION_TITLE", resourceCulture); + } + } + /// /// Looks up a localized string similar to Download.... /// @@ -14094,6 +14103,33 @@ public static string DOWNLOAD_ELLIPSES { } } + /// + /// Looks up a localized string similar to The downloaded file can no longer be found.. + /// + public static string DOWNLOAD_FILE_ACTION_NOT_FOUND { + get { + return ResourceManager.GetString("DOWNLOAD_FILE_ACTION_NOT_FOUND", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Downloading {0} ({1} of {2}).... + /// + public static string DOWNLOAD_FILE_ACTION_PROGRESS_DESCRIPTION { + get { + return ResourceManager.GetString("DOWNLOAD_FILE_ACTION_PROGRESS_DESCRIPTION", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download latest {0} source code. + /// + public static string DOWNLOAD_LATEST_SOURCE { + get { + return ResourceManager.GetString("DOWNLOAD_LATEST_SOURCE", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} is now available. Download the latest at the {1} downloads website.. /// @@ -14103,6 +14139,15 @@ public static string DOWNLOAD_LATEST_XS_BODY { } } + /// + /// Looks up a localized string similar to Download {0} v{1} source code. + /// + public static string DOWNLOAD_SOURCE { + get { + return ResourceManager.GetString("DOWNLOAD_SOURCE", resourceCulture); + } + } + /// /// Looks up a localized string similar to Downloading update from '{0}' ({1} of {2}).... /// diff --git a/XenModel/Messages.resx b/XenModel/Messages.resx index 4a08d6a28e..c0ca9f5923 100755 --- a/XenModel/Messages.resx +++ b/XenModel/Messages.resx @@ -4965,21 +4965,36 @@ Plugging in untrustworthy USB devices to your computer may put your computer at Downloading {0} installer - - Downloading {0} installer ({1} of {2})... - Download {0} installer The downloaded installer can no longer be found. + + Downloading {0} source code + + + Download {0} source code + Download... + + The downloaded file can no longer be found. + + + Downloading {0} ({1} of {2})... + + + Download latest {0} source code + {0} is now available. Download the latest at the {1} downloads website. + + Download {0} v{1} source code + Downloading update from '{0}' ({1} of {2})... diff --git a/XenModel/XenModel.csproj b/XenModel/XenModel.csproj index 13349a2b82..b0b30a5cab 100755 --- a/XenModel/XenModel.csproj +++ b/XenModel/XenModel.csproj @@ -131,7 +131,7 @@ - + diff --git a/scripts/xenadmin-build.ps1 b/scripts/xenadmin-build.ps1 index 59ec9f3e50..489a06f0b5 100644 --- a/scripts/xenadmin-build.ps1 +++ b/scripts/xenadmin-build.ps1 @@ -310,18 +310,20 @@ $xmlFormat=@" url="{2}" checksum="{3}" value="{4}" + sourceUrl="{5}" /> "@ $msi_url = $XC_UPDATES_URL -replace "XCUpdates.xml","$appName.msi" +$source_url = $XC_UPDATES_URL -replace "XCUpdates.xml","$appName-source.zip" $date=(Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") $productVersion = "$BRANDING_XC_PRODUCT_VERSION.$buildNumber" $productFullName = "$appName $productVersion" -$prodXml = [string]::Format($xmlFormat, $productFullName, $date, $msi_url, $msi_checksum, $productVersion) -$testXml = [string]::Format($xmlFormat, $productFullName, $date, "@DEV_MSI_URL_PLACEHOLDER@", $msi_checksum, $productVersion) +$prodXml = [string]::Format($xmlFormat, $productFullName, $date, $msi_url, $msi_checksum, $productVersion, $source_url) +$testXml = [string]::Format($xmlFormat, $productFullName, $date, "@DEV_MSI_URL_PLACEHOLDER@", $msi_checksum, $productVersion, "@DEV_SRC_URL_PLACEHOLDER@") Write-Host $prodXml Write-Host $testXml @@ -331,7 +333,9 @@ Write-Host $testXml $bomLessEncoding = New-Object System.Text.UTF8Encoding $false Write-Host "INFO: Generating XCUpdates.xml" + [System.IO.File]::WriteAllLines("$OUTPUT_DIR\XCUpdates.xml", $prodXml, $bomLessEncoding) Write-Host "INFO: Generating stage-test-XCUpdates.xml. URL is a placeholder value" + [System.IO.File]::WriteAllLines("$OUTPUT_DIR\stage-test-XCUpdates.xml", $testXml, $bomLessEncoding)