/* 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; using System.Collections.Generic; using System.Globalization; using System.Xml; namespace XenAdmin { public enum RoundingBehaviour { Up, Down, Nearest, None } /// /// Miscellaneous utility functions /// public static class Util { public const long BINARY_KILO = 1024; public const long BINARY_MEGA = BINARY_KILO * BINARY_KILO; public const long BINARY_GIGA = BINARY_KILO * BINARY_MEGA; public const long BINARY_TERA = BINARY_KILO * BINARY_GIGA; public const long BINARY_PETA = BINARY_KILO * BINARY_TERA; public const long DEC_KILO = 1000; public const long DEC_MEGA = DEC_KILO * DEC_KILO; public const long DEC_GIGA = DEC_KILO * DEC_MEGA; public const long DEC_TERA = DEC_KILO * DEC_GIGA; /// /// The default iSCSI filer port. /// public const UInt16 DEFAULT_ISCSI_PORT = 3260; public static string MemorySizeStringSuitableUnits(double bytes, bool showPoint0Decimal) { if (bytes >= 1 * BINARY_GIGA) { string format = Messages.VAL_GB_ONE_DECIMAL; int dp = 1; double valGB = bytes / BINARY_GIGA; if (valGB > 100) { dp = 0; format = Messages.VAL_GB; } if(!showPoint0Decimal) { format = Messages.VAL_GB; } return string.Format(format, Math.Round(valGB, dp, MidpointRounding.AwayFromZero)); } else if (bytes >= 1 * BINARY_MEGA) { return string.Format(Messages.VAL_MB, Math.Round(bytes / BINARY_MEGA)); } else if (bytes >= 1 * BINARY_KILO) { return string.Format(Messages.VAL_KB, Math.Round(bytes / BINARY_KILO)); } if(bytes == 0) { return bytes.ToString(); } else { return string.Format(Messages.VAL_B, bytes); } } /// /// Returns the string in suitable units and when the number of bytes is 0 the units are the ones given in formatStringWhenZero. /// E.g. : if bytes = 0, showPoint0Decimal = false, formatStringWhenZero = Messages.VAL_MB, then the function will return "0 MB". /// /// /// /// /// public static string MemorySizeStringSuitableUnits(double bytes, bool showPoint0Decimal, string formatStringWhenZero) { if (bytes == 0) { return string.Format(formatStringWhenZero, bytes); } else { return MemorySizeStringSuitableUnits(bytes, showPoint0Decimal); } } public static string DataRateString(double bytesPerSec) { string unit; string value = ByteSizeString(bytesPerSec, 1, true, out unit); return string.Format(Messages.VAL_FORMAT, value, unit); } public static string DataRateValue(double bytesPerSec, out string unit) { return ByteSizeString(bytesPerSec, 1, true, out unit); } public static string DiskSizeString(ulong bytes) { string value = ByteSizeString(bytes, 1, false, out var unit); return string.Format(Messages.VAL_FORMAT, value, unit); } public static string DiskSizeString(long bytes, string format = null) { return DiskSizeString(bytes, 1, format); } public static string DiskSizeString(long bytes, int dp, string format = null) { ulong abs = (ulong)Math.Abs(bytes); string unit; string value = ByteSizeString(abs, dp, false, out unit, format); return string.Format(Messages.VAL_FORMAT, value, unit); } public static string DiskSizeStringWithoutUnits(long bytes) { return ByteSizeString(bytes, 1, false, out _); } public static string MemorySizeStringVariousUnits(double bytes) { string unit; string value = ByteSizeString(bytes, 0, false, out unit); return string.Format(Messages.VAL_FORMAT, value, unit); } public static string MemorySizeValueVariousUnits(double bytes, out string unit) { return ByteSizeString(bytes, 0, false, out unit); } private static string ByteSizeString(double bytes, int decPlaces, bool isRate, out string unit, string format = null) { if (isRate) { if (bytes >= DEC_TERA) { unit = Messages.VAL_TERRATE; var result = Math.Round(bytes / DEC_TERA, decPlaces); return string.IsNullOrEmpty(format) ? result.ToString() : result.ToString(format); } if (bytes >= DEC_GIGA) { unit = Messages.VAL_GIGRATE; var result = Math.Round(bytes / DEC_GIGA, decPlaces); return string.IsNullOrEmpty(format) ? result.ToString() : result.ToString(format); } if (bytes >= DEC_MEGA) { unit = Messages.VAL_MEGRATE; var result = Math.Round(bytes / DEC_MEGA, decPlaces); return string.IsNullOrEmpty(format) ? result.ToString() : result.ToString(format); } if (bytes >= DEC_KILO) { unit = Messages.VAL_KILRATE; var result = Math.Round(bytes / DEC_KILO, decPlaces); return string.IsNullOrEmpty(format) ? result.ToString() : result.ToString(format); } unit = Messages.VAL_RATE; return bytes.ToString(); } if (bytes >= BINARY_TERA) { unit = Messages.VAL_TERB; var result = Math.Round(bytes / BINARY_TERA, decPlaces); return string.IsNullOrEmpty(format) ? result.ToString() : result.ToString(format); } if (bytes >= BINARY_GIGA) { unit = Messages.VAL_GIGB; var result = Math.Round(bytes / BINARY_GIGA, decPlaces); return string.IsNullOrEmpty(format) ? result.ToString() : result.ToString(format); } if (bytes >= BINARY_MEGA) { unit = Messages.VAL_MEGB; var result = Math.Round(bytes / BINARY_MEGA, decPlaces); return string.IsNullOrEmpty(format) ? result.ToString() : result.ToString(format); } if (bytes >= BINARY_KILO) { unit = Messages.VAL_KILB; var result = Math.Round(bytes / BINARY_KILO, decPlaces); return string.IsNullOrEmpty(format) ? result.ToString() : result.ToString(format); } unit = Messages.VAL_BYTE; return bytes.ToString(); } /// /// nothing actually is measured in nanoseconds, we actually output microseconds /// public static string NanoSecondsString(double t) { string unit; string value = NanoSecondsValue(t, out unit); return string.Format(Messages.VAL_FORMAT_SECONDS, value, unit); } public static string NanoSecondsValue(double t, out string unit) { if (t >= DEC_GIGA) { unit = Messages.VAL_SEC; return (t / DEC_GIGA).ToString("0"); } if (t >= DEC_MEGA) { unit = Messages.VAL_MILSEC; return (t / DEC_MEGA).ToString("0"); } if (t >= DEC_KILO) { unit = Messages.VAL_MICSEC; return (t / DEC_KILO).ToString("0"); } unit = Messages.VAL_NANOSEC; return t.ToString("0"); } public static string MilliWattString(double t) { string unit; string value = MilliWattValue(t, out unit); return string.Format(Messages.VAL_FORMAT, value, unit); } public static string MilliWattValue(double t, out string unit) { if (t >= DEC_GIGA) { unit = Messages.VAL_MWATT; return (t / DEC_GIGA).ToString("0"); } if (t >= DEC_MEGA) { unit = Messages.VAL_KILOWATT; return (t / DEC_MEGA).ToString("0"); } if (t >= DEC_KILO) { unit = Messages.VAL_WATT; return (t / DEC_KILO).ToString("0"); } unit = Messages.VAL_MILWATT; return t.ToString("0"); } private static double DecimalAdjustment(double value, RoundingBehaviour rounding, int decimalPlaces) { int decimalsAdjustment = (int)Math.Pow(10, decimalPlaces); switch (rounding) { case RoundingBehaviour.None: return value; case RoundingBehaviour.Down: return Math.Floor(value * decimalsAdjustment) / decimalsAdjustment; case RoundingBehaviour.Up: return Math.Ceiling(value * decimalsAdjustment) / decimalsAdjustment; case RoundingBehaviour.Nearest: default: return (Math.Round(value, decimalPlaces, MidpointRounding.AwayFromZero)); } } public static double ToTB(double bytes, RoundingBehaviour rounding, int decimalPlaces) { double value = bytes / BINARY_TERA; return DecimalAdjustment(value, rounding, decimalPlaces); } public static double ToGB(double bytes, RoundingBehaviour rounding, int decimalPlaces) { double value = bytes / BINARY_GIGA; return DecimalAdjustment(value, rounding, decimalPlaces); } public static double ToMB(double bytes, RoundingBehaviour rounding, int decimalPlaces = 0) { double value = bytes / BINARY_MEGA; return DecimalAdjustment(value, rounding, decimalPlaces); } public static double CorrectRoundingErrors(double amount) { // Special case to cope with choosing an amount that's a multiple of 0.1G but not 0.5G -- // sending it to the server as the nearest byte and getting it back later -- // and finding it's fractionally changed, messing up our spinner permitted ranges. double amountRounded = ToGB(amount, RoundingBehaviour.Nearest, 1) * BINARY_GIGA; double roundingDiff = amountRounded - amount; if (roundingDiff > -1.0 && roundingDiff < 1.0) // within 1 byte: although I think it will always be positive in the case we want to correct return amountRounded; else return amount; } #region DateTime Utils public const long TicksBefore1970 = 621355968000000000; public static readonly string[] Iso8601DateFormats = {"yyyyMMddTHH:mm:ssZ", "yyyy-MM-ddTHH:mm:ssZ"}; public static long TicksToSecondsSince1970(long ticks) { return (long)Math.Floor(new TimeSpan(ticks - (TicksBefore1970)).TotalSeconds); } public static bool TryParseIso8601DateTime(string toParse, out DateTime result) { return DateTime.TryParseExact(toParse, Iso8601DateFormats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out result); } public static string ToISO8601DateTime(DateTime t) { return t.ToUniversalTime().ToString(Iso8601DateFormats[0], CultureInfo.InvariantCulture); } public static double ToUnixTime(DateTime time) { TimeSpan diff = time - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); return diff.TotalSeconds; } public static DateTime FromUnixTime(double time) { DateTime bootTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); return bootTime.AddSeconds(time); } public static string TimeString(long t) { if (t >= 120) return string.Format(Messages.TIME_MINUTES, t / 60); if (t > 0) return string.Format(Messages.TIME_SECONDS, t); return Messages.TIME_NEGLIGIBLE; } #endregion internal static string GThanSize(long min) { return string.Format(Messages.GREATER_THAN, DiskSizeString(min)); } internal static string LThanSize(long max) { return string.Format(Messages.LESS_THAN, DiskSizeString(max)); } public static string CountsPerSecondString(double p) { return string.Format(Messages.VAL_FORMAT, p, Messages.COUNTS_PER_SEC_UNIT); } public static string MegaHertzString(double t) { string unit; string value = MegaHertzValue(t, out unit); return string.Format(Messages.VAL_FORMAT, value, unit); } public static string MegaHertzValue(double t, out string unit) { return MegaHertzValue(t, 4, out unit); } /// /// Converts the input value from MHz to GHz if needed, rounding it to the specified decimal places /// private static string MegaHertzValue(double t, int decPlaces, out string unit) { if (t >= DEC_KILO) { unit = Messages.VAL_GIGHZ; return Math.Round(t / DEC_KILO, decPlaces).ToString(); } unit = Messages.VAL_MEGHZ; return Math.Round(t, decPlaces).ToString(); } public static string PercentageString(double fraction) { return string.Format("{0}%", (fraction * 100d).ToString("0.0")); } public static void ThrowIfParameterNull(object obj, string name) { if (name == null) { throw new ArgumentNullException("name"); } if (name.Length == 0) { ThrowBecauseZeroLength("name"); } if (obj == null) { throw new ArgumentNullException(name); } } public static void ThrowIfStringParameterNullOrEmpty(string value, string name) { ThrowIfParameterNull(value, name); if (value.Length == 0) { ThrowBecauseZeroLength(name); } } public static void ThrowIfEnumerableParameterNullOrEmpty(IEnumerable value, string name) { ThrowIfParameterNull(value, name); #pragma warning disable 0168 foreach (object _ in value) { return; } #pragma warning restore 0168 ThrowBecauseZeroLength(name); } private static void ThrowBecauseZeroLength(string name) { throw new ArgumentException(string.Format("{0} cannot have 0 length.", name), name); } /// /// Loads the specified non-generic IEnumerable into a generic List<T>. /// /// The type to convert each element to /// The input non-generic IEnumerable. /// Generic List<T> public static List PopulateList(IEnumerable input) { ThrowIfParameterNull(input, "input"); List output = new List(); foreach (T t in input) { output.Add(t); } return output; } /// /// Gets a List that represents the specified IEnumerable. If the input isn't a List then one gets created by passing the input into /// List's constructor. If the input is already a List then it is returned directly. /// /// /// The input. /// A list for the specified enumerable public static List GetList(IEnumerable input) { if (input == null) { return null; } var list = input as List; if (list != null) { return list; } return new List(input); } /// /// Matches 1-65535 inclusive. /// /// The string to be parsed /// True if the specified string contains only a valid port, otherwise false. public static bool IsValidPort(string s) { int port; if (!int.TryParse(s, out port)) return false; return 0 < port && port <= 65535; } public static string GetXmlNodeInnerText(XmlNode node, string xPath) { ThrowIfParameterNull(node, "node"); ThrowIfStringParameterNullOrEmpty(xPath, "xPath"); XmlNodeList nodes = node.SelectNodes(xPath); if (nodes == null || nodes.Count == 0) { throw new InvalidOperationException("Node not found: " + xPath); } return nodes[0].InnerText; } /// /// Get the first node with name 'value' and returns its innerText. Used for getting results of CGSL async actions. /// /// The XML. /// The contents of the first node with name 'value'. public static string GetContentsOfValueNode(string xml) { ThrowIfStringParameterNullOrEmpty(xml, "xml"); XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); // If we've got this from an async task result, then it will be wrapped // in a element. foreach (XmlNode node in doc.GetElementsByTagName("value")) { return node.InnerText; } return null; } } }