/* 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.Collections.Generic; using System.Reflection; using System.Threading; using log4net; using XenAdmin.Wizards.PatchingWizard.PlanActions; using XenAPI; namespace XenAdmin.Wizards.RollingUpgradeWizard.PlanActions { class SemiAutomaticBackgroundThread { private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private Thread _bw; private IDictionary> _planActions; private PlanAction _revertAction; private IEnumerable _mastersToUpgrade; public event EventHandler ReportHostDone; public event EventHandler ManageSemiAutomaticPlanAction; public event EventHandler ReportRunning; public event EventHandler ReportException; public event EventHandler ReportRevertDone; public event EventHandler Completed; public SemiAutomaticBackgroundThread(IEnumerable mastersToUpgrade, IDictionary> planActions, PlanAction revertAction) { _planActions = planActions; _mastersToUpgrade = mastersToUpgrade; _revertAction = revertAction; _bw = new Thread(BworkerDoWork) { IsBackground = true }; } public void Start() { _bw.Start(); } private static List GetAllHosts(IEnumerable masters) { var hosts = new List(); foreach (var master in masters) { hosts.Add(master); foreach (var host in master.Connection.Cache.Hosts) { if (host != master) hosts.Add(host); } } return hosts; } private static bool CheckMasterIsUpgraded(List hosts, int i) { string masterVersion = hosts[i].LongProductVersion; for (int j = i + 1; j < hosts.Count; j++) { if (hosts[j].LongProductVersion != masterVersion) return true; if (hosts[j].IsMaster()) break; } return false; } private static int SkipSlaves(List hosts, int i) { for (int j = i + 1; j < hosts.Count; j++) { if (hosts[j].IsMaster()) break; i++; } return i; } private void BworkerDoWork() { Host currentHost = null; try { List hosts = GetAllHosts(_mastersToUpgrade); bool currentMasterFailed = false; string poolHigherProductVersion = string.Empty; for (int i = 0; i < hosts.Count; i++) { if (_cancel) return; //Skip hosts already upgraded var host=currentHost = hosts[i]; if (host.IsMaster()) { poolHigherProductVersion = host.LongProductVersion; if (CheckMasterIsUpgraded(hosts, i)) { log.Debug(string.Format("Skipping master '{0}' because it is upgraded", host.Name)); continue; } } else if (host.LongProductVersion == poolHigherProductVersion) { log.Debug(string.Format("Skipping host '{0}' because it is upgraded", host.Name)); continue; } log.Debug(string.Format("Starting to upgrade host '{0}'", host.Name)); //Add subtasks for the current host bool allActionsDone = true; foreach (var planAction in _planActions[host]) { //if the wizard has been cancelled, skip this action, unless it is a BringBabiesBackAction if (_cancel && !(planAction is BringBabiesBackAction)) continue; PlanAction action = planAction; ReportRunning.Raise(this, () => new ReportRunningArgs(action, host)); try { if (planAction is UpgradeManualHostPlanAction) { var upgradeAction = (UpgradeManualHostPlanAction)planAction; ManageSemiAutomaticPlanAction.Raise(this, () => new ManageSemiAutomaticPlanActionArgs(upgradeAction)); if (host.IsMaster()) poolHigherProductVersion = upgradeAction.Host.LongProductVersion; } else planAction.Run(); } catch (Exception excep) { log.Error(string.Format("Exception in host '{0}' while it was upgraded", host.Name), excep); PlanAction action1 = planAction; ReportException.Raise(this, () => new ReportExceptionArgs(excep, action1, host)); if (host.IsMaster()) currentMasterFailed = true; allActionsDone = false; break; } } if (allActionsDone) { ReportHostDone.Raise(this, () => new ReportHostDoneArgs(host)); } //Skip slaves if master failed if (currentMasterFailed) { i = SkipSlaves(hosts, i); } } } catch (Exception excep) { log.Error("Upgrade thread error: ", excep); } finally { //Revert resolved prechecks try { log.Debug("Reverting prechecks"); ReportRunning.Raise(this, () => new ReportRunningArgs(_revertAction, currentHost)); _revertAction.Run(); ReportRevertDone.Raise(this, () => new ReportRevertDoneArgs(_revertAction)); } catch (Exception excep) { log.Error("Exception reverting prechecks", excep); ReportException.Raise(this, () => new ReportExceptionArgs(excep, _revertAction, currentHost)); } Completed.Raise(this); } } private volatile bool _cancel = false; internal void Cancel() { _cancel = true; } } internal class ReportRevertDoneArgs : EventArgs { public ReportRevertDoneArgs(PlanAction planAction) { PlanAction = planAction; } public PlanAction PlanAction { get; private set; } } internal class ReportExceptionArgs : EventArgs { public ReportExceptionArgs(Exception excep, PlanAction action, Host host) { Exception = excep; Action = action; Host = host; } public Exception Exception { get; private set; } public PlanAction Action { get; private set; } public Host Host { get; private set; } } internal class ReportRunningArgs : EventArgs { public ReportRunningArgs(PlanAction action, Host host) { Host = host; Action = action; } public Host Host { get; private set; } public PlanAction Action { get; private set; } } internal class ManageSemiAutomaticPlanActionArgs : EventArgs { public ManageSemiAutomaticPlanActionArgs(UpgradeManualHostPlanAction action) { Action = action; } public UpgradeManualHostPlanAction Action { get; private set; } } internal class ReportHostDoneArgs : EventArgs { public ReportHostDoneArgs(Host host) { Host = host; } public Host Host { get; private set; } } }