2017-01-16 20:59:50 +01:00
|
|
|
|
/* Copyright (c) Citrix Systems, Inc.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
* 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<string> InstalledHotfixes { get; set; }
|
|
|
|
|
private bool CheckHotfixContents{ get; set; }
|
|
|
|
|
|
|
|
|
|
public CFUValidator(OptionUsage urlOrFile, string xmlLocation, string serverVersion,
|
|
|
|
|
List<string> 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<XenServerPatch> xenServerPatches;
|
|
|
|
|
List<XenServerVersion> xenServerVersions;
|
|
|
|
|
List<XenCenterVersion> xenCenterVersions;
|
|
|
|
|
|
|
|
|
|
Status = "Getting check for updates XML from " + XmlLocation + "...";
|
|
|
|
|
ReadCheckForUpdatesXML(out xenServerPatches, out xenServerVersions, out xenCenterVersions);
|
|
|
|
|
|
|
|
|
|
List<string> versionToCheck = GetVersionToCheck(xenServerVersions);
|
|
|
|
|
|
|
|
|
|
foreach (string ver in versionToCheck)
|
|
|
|
|
{
|
|
|
|
|
ServerVersion = ver;
|
|
|
|
|
RunTestsForGivenServerVersion(xenServerVersions, xenServerPatches, xenCenterVersions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private List<string> GetVersionToCheck(List<XenServerVersion> xenServerVersions)
|
|
|
|
|
{
|
|
|
|
|
if(ServerVersion == CFUCommandLineOptionManager.AllVersions)
|
|
|
|
|
return xenServerVersions.ConvertAll(i => i.Version.ToString()).Distinct().ToList();
|
|
|
|
|
|
|
|
|
|
return new List<string>{ServerVersion};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RunTestsForGivenServerVersion(List<XenServerVersion> xenServerVersions,
|
|
|
|
|
List<XenServerPatch> xenServerPatches,
|
|
|
|
|
List<XenCenterVersion> xenCenterVersions)
|
|
|
|
|
{
|
|
|
|
|
CheckProvidedVersionNumber(xenServerVersions);
|
|
|
|
|
|
|
|
|
|
Status = String.Format("Generating server {0} mock-ups...", ServerVersion);
|
|
|
|
|
SetupMocks(xenServerPatches, xenServerVersions);
|
|
|
|
|
|
|
|
|
|
Status = "Determining XenCenter update required...";
|
2017-03-14 16:25:49 +01:00
|
|
|
|
var xcupdateAlerts = XenAdmin.Core.Updates.NewXenCenterUpdateAlerts(xenCenterVersions, new Version(ServerVersion));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
Status = "Determining XenServer update required...";
|
2017-03-14 17:38:10 +01:00
|
|
|
|
var updateAlerts = XenAdmin.Core.Updates.NewXenServerVersionAlerts(xenServerVersions);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
Status = "Determining patches required...";
|
2014-01-08 14:17:02 +01:00
|
|
|
|
var patchAlerts = XenAdmin.Core.Updates.NewXenServerPatchAlerts(xenServerVersions, xenServerPatches).Where(alert => !alert.CanIgnore).ToList();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
//Build patch checks list
|
|
|
|
|
List<AlertFeatureValidator> validators = new List<AlertFeatureValidator>
|
|
|
|
|
{
|
|
|
|
|
new CorePatchDetailsValidator(patchAlerts),
|
|
|
|
|
new PatchURLValidator(patchAlerts),
|
|
|
|
|
new ZipContentsValidator(patchAlerts)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Status = "Running patch check(s), this may take some time...";
|
|
|
|
|
RunValidators(validators);
|
|
|
|
|
|
|
|
|
|
Status = "Generating summary...";
|
2017-03-14 17:38:10 +01:00
|
|
|
|
GeneratePatchSummary(patchAlerts, validators, updateAlerts, xcupdateAlerts);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CheckProvidedVersionNumber(List<XenServerVersion> 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<AlertFeatureValidator> 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<XenServerPatchAlert> alerts, List<AlertFeatureValidator> validators,
|
2017-03-14 17:38:10 +01:00
|
|
|
|
List<XenServerVersionAlert> updateAlerts, List<XenCenterUpdateAlert> xcupdateAlerts)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
OuputComponent oc = new OutputTextOuputComponent(XmlLocation, ServerVersion);
|
2017-03-14 16:25:49 +01:00
|
|
|
|
XenCenterUpdateDecorator xcud = new XenCenterUpdateDecorator(oc, xcupdateAlerts);
|
2017-03-14 17:38:10 +01:00
|
|
|
|
XenServerUpdateDecorator xsud = new XenServerUpdateDecorator(xcud, updateAlerts);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
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<XenServerPatch> patches, out List<XenServerVersion> versions, out List<XenCenterVersion> 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<XenServerPatch> xenServerPatches, List<XenServerVersion> xenServerVersions)
|
|
|
|
|
{
|
|
|
|
|
Mock<Host> master = mom.NewXenObject<Host>(id);
|
|
|
|
|
Mock<Pool> pool = mom.NewXenObject<Pool>(id);
|
|
|
|
|
XenRef<Host> masterRef = new XenRef<Host>("ref");
|
|
|
|
|
pool.Setup(p => p.master).Returns(masterRef);
|
|
|
|
|
mom.MockCacheFor(id).Setup(c => c.Resolve(It.IsAny<XenRef<Pool>>())).Returns(pool.Object);
|
|
|
|
|
mom.MockConnectionFor(id).Setup(c => c.Resolve(masterRef)).Returns(master.Object);
|
|
|
|
|
master.Setup(h => h.software_version).Returns(new Dictionary<string, string>());
|
|
|
|
|
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<Pool_patch> GenerateMockPoolPatches(List<XenServerPatch> xenServerPatches)
|
|
|
|
|
{
|
|
|
|
|
List<Pool_patch> patches = new List<Pool_patch>();
|
|
|
|
|
|
|
|
|
|
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<Pool_patch> pp = mom.NewXenObject<Pool_patch>(id);
|
|
|
|
|
pp.Setup(p => p.uuid).Returns(match.Uuid);
|
|
|
|
|
patches.Add(pp.Object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return patches;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|