mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-03 16:41:04 +01:00
7c0bc50b4a
Inc. Signed-off-by: Gabor Apati-Nagy<gabor.apati-nagy@citrix.com>
387 lines
16 KiB
C#
387 lines
16 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.Collections.Generic;
|
|
using XenAPI;
|
|
using XenAdmin.Network;
|
|
using XenAdmin.Core;
|
|
using System.Globalization;
|
|
|
|
namespace XenAdmin.Model
|
|
{
|
|
public enum HealthCheckStatus
|
|
{
|
|
Disabled, Enabled, Undefined
|
|
}
|
|
|
|
public enum DiagnosticAlertSeverity
|
|
{
|
|
Info, Warning, Error
|
|
}
|
|
|
|
public class HealthCheckSettings
|
|
{
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
public HealthCheckStatus Status;
|
|
public int IntervalInDays, TimeOfDay, RetryInterval;
|
|
public DayOfWeek DayOfWeek;
|
|
public string UploadTokenSecretUuid;
|
|
public string NewUploadRequest;
|
|
public string UserNameSecretUuid;
|
|
public string PasswordSecretUuid;
|
|
public string LastSuccessfulUpload;
|
|
public string LastFailedUpload;
|
|
public string DiagnosticTokenSecretUuid;
|
|
public string UploadUuid;
|
|
public DiagnosticAlertSeverity ReportAnalysisSeverity;
|
|
public int ReportAnalysisIssuesDetected;
|
|
public string ReportAnalysisUploadUuid;
|
|
public string ReportAnalysisUploadTime;
|
|
|
|
public const int DEFAULT_INTERVAL_IN_DAYS = 14;
|
|
public const int DEFAULT_RETRY_INTERVAL = 7; // in days
|
|
public const int UPLOAD_REQUEST_VALIDITY_INTERVAL = 30; // in minutes
|
|
|
|
public const string STATUS = "Enrollment";
|
|
public const string INTERVAL_IN_DAYS = "Schedule.IntervalInDays";
|
|
public const string DAY_OF_WEEK = "Schedule.DayOfWeek";
|
|
public const string TIME_OF_DAY = "Schedule.TimeOfDay";
|
|
public const string RETRY_INTERVAL = "Schedule.RetryInterval";
|
|
public const string UPLOAD_TOKEN_SECRET = "UploadToken.Secret";
|
|
public const string UPLOAD_CREDENTIAL_USER_SECRET = "User.Secret";
|
|
public const string UPLOAD_CREDENTIAL_PASSWORD_SECRET = "Password.Secret";
|
|
public const string UPLOAD_LOCK = "UploadLock";
|
|
public const string LAST_SUCCESSFUL_UPLOAD = "LastSuccessfulUpload";
|
|
public const string LAST_FAILED_UPLOAD = "LastFailedUpload";
|
|
public const string NEW_UPLOAD_REQUEST = "NewUploadRequest";
|
|
public const string HEALTH_CHECK_PIPE = "HealthCheckServicePipe";
|
|
public const string HEALTH_CHECK_PIPE_END_MESSAGE = "HealthCheckServicePipe";
|
|
public const string UPLOAD_UUID = "UploadUuid";
|
|
public const string PROXY_SETTINGS = "ProxySettings";
|
|
public const string DIAGNOSTIC_TOKEN_SECRET = "DiagnosticToken.Secret";
|
|
public const string REPORT_ANALYSIS_SEVERITY = "ReportAnalysis.Severity";
|
|
public const string REPORT_ANALYSIS_ISSUES_DETECTED = "ReportAnalysis.IssuesDetected";
|
|
public const string REPORT_ANALYSIS_UPLOAD_UUID = "ReportAnalysis.UploadUuid";
|
|
public const string REPORT_ANALYSIS_UPLOAD_TIME = "ReportAnalysis.UploadTime";
|
|
|
|
|
|
private const string REPORT_LINK_DOMAIN_NAME = "https://cis.citrix.com";
|
|
private const string REPORT_LINK_PATH = "AutoSupport/analysis/upload_overview";
|
|
|
|
public HealthCheckSettings(Dictionary<string, string> config)
|
|
{
|
|
Status = config == null || !config.ContainsKey(STATUS)
|
|
? HealthCheckStatus.Undefined
|
|
: (BoolKey(config, STATUS) ? HealthCheckStatus.Enabled : HealthCheckStatus.Disabled);
|
|
IntervalInDays = IntKey(config, INTERVAL_IN_DAYS, DEFAULT_INTERVAL_IN_DAYS);
|
|
if (!Enum.TryParse(Get(config, DAY_OF_WEEK), out DayOfWeek))
|
|
DayOfWeek = (DayOfWeek) GetDefaultDay();
|
|
TimeOfDay = IntKey(config, TIME_OF_DAY, GetDefaultTime());
|
|
RetryInterval = IntKey(config, RETRY_INTERVAL, DEFAULT_RETRY_INTERVAL);
|
|
UploadTokenSecretUuid = Get(config, UPLOAD_TOKEN_SECRET);
|
|
DiagnosticTokenSecretUuid = Get(config, DIAGNOSTIC_TOKEN_SECRET);
|
|
NewUploadRequest = Get(config, NEW_UPLOAD_REQUEST);
|
|
UserNameSecretUuid = Get(config, UPLOAD_CREDENTIAL_USER_SECRET);
|
|
PasswordSecretUuid = Get(config, UPLOAD_CREDENTIAL_PASSWORD_SECRET);
|
|
LastSuccessfulUpload = Get(config, LAST_SUCCESSFUL_UPLOAD);
|
|
LastFailedUpload = Get(config, LAST_FAILED_UPLOAD);
|
|
UploadUuid = Get(config, UPLOAD_UUID);
|
|
ReportAnalysisSeverity = StringToDiagnosticAlertSeverity(Get(config, REPORT_ANALYSIS_SEVERITY));
|
|
ReportAnalysisIssuesDetected = IntKey(config, REPORT_ANALYSIS_ISSUES_DETECTED, 0);
|
|
ReportAnalysisUploadUuid = Get(config, REPORT_ANALYSIS_UPLOAD_UUID);
|
|
ReportAnalysisUploadTime = Get(config, REPORT_ANALYSIS_UPLOAD_TIME);
|
|
}
|
|
|
|
public Dictionary<string, string> ToDictionary(Dictionary<string, string> baseDictionary)
|
|
{
|
|
var newConfig = baseDictionary == null ? new Dictionary<string, string>() : new Dictionary<string, string>(baseDictionary);
|
|
newConfig[STATUS] = Status == HealthCheckStatus.Enabled ? "true" : "false";
|
|
newConfig[INTERVAL_IN_DAYS] = IntervalInDays.ToString();
|
|
var day = (int) DayOfWeek;
|
|
newConfig[DAY_OF_WEEK] = day.ToString();
|
|
newConfig[TIME_OF_DAY] = TimeOfDay.ToString();
|
|
newConfig[RETRY_INTERVAL] = RetryInterval.ToString();
|
|
newConfig[UPLOAD_TOKEN_SECRET] = UploadTokenSecretUuid;
|
|
newConfig[DIAGNOSTIC_TOKEN_SECRET] = DiagnosticTokenSecretUuid;
|
|
newConfig[NEW_UPLOAD_REQUEST] = NewUploadRequest;
|
|
return newConfig;
|
|
}
|
|
|
|
public int IntervalInWeeks { get { return IntervalInDays / 7; } }
|
|
|
|
public string StatusDescription
|
|
{
|
|
get
|
|
{
|
|
return Status == HealthCheckStatus.Enabled
|
|
? ReportAnalysisStatus
|
|
: Messages.HEALTHCHECK_STATUS_NOT_ENROLLED;
|
|
}
|
|
}
|
|
|
|
public bool CanRequestNewUpload
|
|
{
|
|
get
|
|
{
|
|
if (Status != HealthCheckStatus.Enabled)
|
|
return false;
|
|
var uploadRequestExpiryTime = NewUploadRequestTime.AddMinutes(UPLOAD_REQUEST_VALIDITY_INTERVAL);
|
|
return DateTime.Compare(uploadRequestExpiryTime, DateTime.UtcNow) < 0;
|
|
}
|
|
}
|
|
|
|
public DateTime NewUploadRequestTime
|
|
{
|
|
get
|
|
{
|
|
if (!string.IsNullOrEmpty(NewUploadRequest))
|
|
{
|
|
try
|
|
{
|
|
return StringToDateTime(NewUploadRequest);
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Error("Exception while parsing NewUploadRequest", exn);
|
|
}
|
|
}
|
|
return DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
public string ReportAnalysisStatus
|
|
{
|
|
get
|
|
{
|
|
if (HasAnalysisResult)
|
|
{
|
|
switch (ReportAnalysisIssuesDetected)
|
|
{
|
|
case 0:
|
|
return Messages.HEALTHCHECK_STATUS_NO_ISSUES_FOUND;
|
|
case 1:
|
|
return Messages.HEALTHCHECK_STATUS_ONE_ISSUE_FOUND;
|
|
default:
|
|
return String.Format(Messages.HEALTHCHECK_STATUS_ISSUES_FOUND, ReportAnalysisIssuesDetected);
|
|
}
|
|
}
|
|
return HasUpload ? Messages.HEALTHCHECK_STATUS_NOT_AVAILABLE_YET : Messages.HEALTHCHECK_STATUS_NO_UPLOAD_YET;
|
|
}
|
|
}
|
|
|
|
public string GetReportAnalysisLink(string domainName)
|
|
{
|
|
return string.Format("{0}/{1}/{2}", string.IsNullOrEmpty(domainName) ? REPORT_LINK_DOMAIN_NAME : domainName, REPORT_LINK_PATH, ReportAnalysisUploadUuid);
|
|
}
|
|
|
|
public bool HasUpload
|
|
{
|
|
get { return !string.IsNullOrEmpty(UploadUuid); }
|
|
}
|
|
|
|
public bool HasAnalysisResult
|
|
{
|
|
get { return HasUpload && ReportAnalysisUploadUuid == UploadUuid; }
|
|
}
|
|
|
|
public bool HasOldAnalysisResult
|
|
{
|
|
get
|
|
{
|
|
return !HasAnalysisResult && !string.IsNullOrEmpty(ReportAnalysisUploadUuid) &&
|
|
!string.IsNullOrEmpty(ReportAnalysisUploadTime);
|
|
}
|
|
}
|
|
|
|
public DateTime LastFailedUploadTime
|
|
{
|
|
get
|
|
{
|
|
DateTime lastFailedUploadTime;
|
|
return !string.IsNullOrEmpty(LastFailedUpload) && TryParseStringToDateTime(LastFailedUpload, out lastFailedUploadTime)
|
|
? lastFailedUploadTime
|
|
: DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
public DateTime LastSuccessfulUploadTime
|
|
{
|
|
get
|
|
{
|
|
DateTime lastSuccessfulUploadTime;
|
|
return !string.IsNullOrEmpty(LastSuccessfulUpload) && TryParseStringToDateTime(LastSuccessfulUpload, out lastSuccessfulUploadTime)
|
|
? lastSuccessfulUploadTime
|
|
: DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
#region Helper functions
|
|
private static T Get<T>(Dictionary<string, T> d, string k) where T : class
|
|
{
|
|
return d != null && d.ContainsKey(k) ? d[k] : null;
|
|
}
|
|
|
|
private static bool BoolKey(Dictionary<string, string> d, string k)
|
|
{
|
|
string v = Get(d, k);
|
|
return v == null ? false : v == "true";
|
|
}
|
|
|
|
private static int IntKey(Dictionary<string, string> d, string k, int def)
|
|
{
|
|
int result;
|
|
string s = Get(d, k);
|
|
return s != null && int.TryParse(s, out result) ? result : def;
|
|
}
|
|
|
|
private static int GetDefaultDay()
|
|
{
|
|
return new Random().Next(1, 7);
|
|
}
|
|
|
|
public static int GetDefaultTime()
|
|
{
|
|
return new Random().Next(1, 5);
|
|
}
|
|
|
|
public static string DateTimeToString(DateTime dateTime)
|
|
{
|
|
// Round-trip format time
|
|
DateTime rtime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
|
|
return rtime.ToString("o");
|
|
}
|
|
|
|
public static DateTime StringToDateTime(string dateTimeString)
|
|
{
|
|
// Round-trip format time
|
|
DateTime dateTime = DateTime.ParseExact(dateTimeString, "o", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
|
|
return dateTime;
|
|
}
|
|
|
|
public static bool TryParseStringToDateTime(string dateTimeString, out DateTime dateTime)
|
|
{
|
|
// Round-trip format time
|
|
return DateTime.TryParseExact(dateTimeString, "o", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTime);
|
|
}
|
|
|
|
#endregion
|
|
|
|
public string GetSecretyInfo(Session session, string secretType)
|
|
{
|
|
string UUID = string.Empty;
|
|
switch (secretType)
|
|
{
|
|
case HealthCheckSettings.UPLOAD_CREDENTIAL_USER_SECRET:
|
|
UUID = UserNameSecretUuid;
|
|
break;
|
|
case HealthCheckSettings.UPLOAD_CREDENTIAL_PASSWORD_SECRET:
|
|
UUID = PasswordSecretUuid;
|
|
break;
|
|
case HealthCheckSettings.UPLOAD_TOKEN_SECRET:
|
|
UUID = UploadTokenSecretUuid;
|
|
break;
|
|
case HealthCheckSettings.DIAGNOSTIC_TOKEN_SECRET:
|
|
UUID = DiagnosticTokenSecretUuid;
|
|
break;
|
|
default:
|
|
log.ErrorFormat("Error getting the {0} from the xapi secret", secretType);
|
|
break;
|
|
}
|
|
|
|
if (session == null || string.IsNullOrEmpty(UUID))
|
|
return null;
|
|
try
|
|
{
|
|
string opaqueref = Secret.get_by_uuid(session, UUID);
|
|
return Secret.get_value(session, opaqueref);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
log.Error("Exception getting the upload token from the xapi secret", e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public string GetSecretyInfo(IXenConnection connection, string secretType)
|
|
{
|
|
if (connection == null)
|
|
return null;
|
|
return GetSecretyInfo(connection.Session, secretType);
|
|
}
|
|
|
|
public bool TryGetExistingTokens(IXenConnection connection, out string uploadToken, out string diagnosticToken)
|
|
{
|
|
uploadToken = null;
|
|
diagnosticToken = null;
|
|
if (connection == null)
|
|
return false;
|
|
|
|
uploadToken = GetSecretyInfo(connection, UPLOAD_TOKEN_SECRET);
|
|
diagnosticToken = GetSecretyInfo(connection, DIAGNOSTIC_TOKEN_SECRET);
|
|
|
|
if (!String.IsNullOrEmpty(uploadToken) && !String.IsNullOrEmpty(diagnosticToken))
|
|
return true;
|
|
|
|
return TryGetExistingTokensFromOtherConnections(connection, out uploadToken, out diagnosticToken);
|
|
}
|
|
|
|
private static bool TryGetExistingTokensFromOtherConnections(IXenConnection currentConnection, out string uploadToken, out string diagnosticToken)
|
|
{
|
|
uploadToken = null;
|
|
diagnosticToken = null;
|
|
foreach (var connection in ConnectionsManager.XenConnectionsCopy)
|
|
{
|
|
if (connection == currentConnection || !connection.IsConnected)
|
|
continue;
|
|
var poolOfOne = Helpers.GetPoolOfOne(connection);
|
|
if (poolOfOne != null)
|
|
{
|
|
uploadToken = poolOfOne.HealthCheckSettings.GetSecretyInfo(connection, UPLOAD_TOKEN_SECRET);
|
|
diagnosticToken = poolOfOne.HealthCheckSettings.GetSecretyInfo(connection, DIAGNOSTIC_TOKEN_SECRET);
|
|
if (!String.IsNullOrEmpty(uploadToken) && !String.IsNullOrEmpty(diagnosticToken))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal static DiagnosticAlertSeverity StringToDiagnosticAlertSeverity(string severity)
|
|
{
|
|
DiagnosticAlertSeverity result;
|
|
if (Enum.TryParse(severity, out result))
|
|
return result;
|
|
return DiagnosticAlertSeverity.Info;
|
|
}
|
|
}
|
|
}
|