/* 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 System.Text; using CFUValidator.CommandLineOptions; using CFUValidator.OutputDecorators; using CFUValidator.Updates; using CFUValidator.Validators; using Moq; using XenAdmin; using XenAdmin.Alerts; using XenAdmin.Core; using XenAdminTests; using XenAPI; namespace CFUValidator { public class CFUValidationException : Exception { public CFUValidationException(string message) : base(message){} } public class CFUValidator { private readonly MockObjectManager mom = new MockObjectManager(); private readonly XmlRetrieverFactory xmlFactory = new XmlRetrieverFactory(); private const string id = "id"; private string XmlLocation { get; set; } private string ServerVersion { get; set; } private OptionUsage UrlOrFile { get; set; } private List InstalledHotfixes { get; set; } private bool CheckHotfixContents{ get; set; } public CFUValidator(OptionUsage urlOrFile, string xmlLocation, string serverVersion, List installedHotfixes, bool checkHotfixContents) { if(urlOrFile != OptionUsage.File && urlOrFile != OptionUsage.Url) throw new ArgumentException("urlOrFile option should be either File or Url"); mom.CreateNewConnection(id); ConnectionsManager.XenConnections.AddRange(mom.AllConnections); XmlLocation = xmlLocation; ServerVersion = serverVersion; InstalledHotfixes = installedHotfixes; UrlOrFile = urlOrFile; CheckHotfixContents = checkHotfixContents; } public void Run() { List xenServerPatches; List xenServerVersions; List xenCenterVersions; Status = "Getting check for updates XML from " + XmlLocation + "..."; ReadCheckForUpdatesXML(out xenServerPatches, out xenServerVersions, out xenCenterVersions); List versionToCheck = GetVersionToCheck(xenServerVersions); foreach (string ver in versionToCheck) { ServerVersion = ver; RunTestsForGivenServerVersion(xenServerVersions, xenServerPatches, xenCenterVersions); } } private List GetVersionToCheck(List xenServerVersions) { if(ServerVersion == CFUCommandLineOptionManager.AllVersions) return xenServerVersions.ConvertAll(i => i.Version.ToString()).Distinct().ToList(); return new List{ServerVersion}; } private void RunTestsForGivenServerVersion(List xenServerVersions, List xenServerPatches, List xenCenterVersions) { CheckProvidedVersionNumber(xenServerVersions); Status = String.Format("Generating server {0} mock-ups...", ServerVersion); SetupMocks(xenServerPatches, xenServerVersions); Status = "Determining XenCenter update required..."; XenCenterUpdateAlert xcupdateAlert = XenAdmin.Core.Updates.NewXenCenterVersionAlert(xenCenterVersions, new Version(ServerVersion), false); Status = "Determining XenServer update required..."; XenServerUpdateAlert updateAlert = XenAdmin.Core.Updates.NewServerVersionAlert(xenServerVersions, false); Status = "Determining patches required..."; List patchAlerts = XenAdmin.Core.Updates.NewServerPatchesAlerts(xenServerVersions, xenServerPatches, false); //Build patch checks list List validators = new List { new CorePatchDetailsValidator(patchAlerts), new PatchURLValidator(patchAlerts), new ZipContentsValidator(patchAlerts) }; Status = "Running patch check(s), this may take some time..."; RunValidators(validators); Status = "Generating summary..."; GeneratePatchSummary(patchAlerts, validators, updateAlert, xcupdateAlert); } private void CheckProvidedVersionNumber(List xenServerVersions) { if (!xenServerVersions.Any(v => v.Version.ToString() == ServerVersion)) { StringBuilder sb = new StringBuilder("\nAvailable versions are:\n"); xenServerVersions.ConvertAll(i=>i.Version.ToString()).Distinct().ToList().ForEach(v=>sb.AppendLine(v)); throw new CFUValidationException("Could not find the version in the check for updates file: " + ServerVersion + sb); } } public string Output { get; private set; } #region Status event code public delegate void StatusChangedHandler(object sender, EventArgs e); public event StatusChangedHandler StatusChanged; protected virtual void OnStatusChanged() { if (StatusChanged != null) StatusChanged(Status, EventArgs.Empty); } private string status; private string Status { get { return status; } set { status = value; OnStatusChanged(); } } #endregion private void RunValidators(List validators) { int count = 1; foreach (AlertFeatureValidator validator in validators) { if (validator is ZipContentsValidator && !CheckHotfixContents) continue; Status = count++ + ". " + validator.Description + "..."; validator.StatusChanged += validator_StatusChanged; validator.Validate(); validator.StatusChanged -= validator_StatusChanged; } Status = "Validator checks complete"; } private void validator_StatusChanged(object sender, EventArgs e) { Status = sender as string; } private void GeneratePatchSummary(List alerts, List validators, XenServerUpdateAlert updateAlert, XenCenterUpdateAlert xcupdateAlert) { OuputComponent oc = new OutputTextOuputComponent(XmlLocation, ServerVersion); XenCenterUpdateDecorator xcud = new XenCenterUpdateDecorator(oc, xcupdateAlert); XenServerUpdateDecorator xsud = new XenServerUpdateDecorator(xcud, updateAlert); PatchAlertDecorator pad = new PatchAlertDecorator(xsud, alerts); AlertFeatureValidatorDecorator afdCoreFields = new AlertFeatureValidatorDecorator(pad, validators.First(v => v is CorePatchDetailsValidator), "Core fields in patch checks:"); AlertFeatureValidatorDecorator afdPatchUrl = new AlertFeatureValidatorDecorator(afdCoreFields, validators.First(v => v is PatchURLValidator), "Required patch URL checks:"); AlertFeatureValidatorDecorator afdZipContents = new AlertFeatureValidatorDecorator(afdPatchUrl, validators.First(v => v is ZipContentsValidator), "Required patch zip content checks:"); if(CheckHotfixContents) Output = afdZipContents.Generate().Insert(0, Output).ToString(); else Output = afdPatchUrl.Generate().Insert(0, Output).ToString(); } private void ReadCheckForUpdatesXML(out List patches, out List versions, out List xcVersions) { ICheckForUpdatesXMLSource checkForUpdates = xmlFactory.GetAction(UrlOrFile, XmlLocation); checkForUpdates.RunAsync(); ConsoleSpinner spinner = new ConsoleSpinner(); while(!checkForUpdates.IsCompleted) { spinner.Turn(checkForUpdates.PercentComplete); } if (checkForUpdates.ErrorRaised != null) throw checkForUpdates.ErrorRaised; patches = checkForUpdates.XenServerPatches; versions = checkForUpdates.XenServerVersions; xcVersions = checkForUpdates.XenCenterVersions; } private void SetupMocks(List xenServerPatches, List xenServerVersions) { Mock master = mom.NewXenObject(id); Mock pool = mom.NewXenObject(id); XenRef masterRef = new XenRef("ref"); pool.Setup(p => p.master).Returns(masterRef); mom.MockCacheFor(id).Setup(c => c.Resolve(It.IsAny>())).Returns(pool.Object); mom.MockConnectionFor(id).Setup(c => c.Resolve(masterRef)).Returns(master.Object); master.Setup(h => h.software_version).Returns(new Dictionary()); master.Setup(h => h.ProductVersion).Returns(ServerVersion); master.Setup(h => h.AppliedPatches()).Returns(GenerateMockPoolPatches(xenServerPatches)); //Currently build number will be referenced first so if it's present hook it up string buildNumber = xenServerVersions.First(v => v.Version.ToString() == ServerVersion).BuildNumber; master.Setup(h=>h.BuildNumberRaw).Returns(buildNumber); } private List GenerateMockPoolPatches(List xenServerPatches) { List patches = new List(); foreach (string installedHotfix in InstalledHotfixes) { string hotfix = installedHotfix; XenServerPatch match = xenServerPatches.Find(m => m.Name.Contains(hotfix)); if(match == null) throw new CFUValidationException("No patch could be found in the XML matching " + hotfix); Mock pp = mom.NewXenObject(id); pp.Setup(p => p.uuid).Returns(match.Uuid); patches.Add(pp.Object); } return patches; } } }