2023-01-24 15:29:31 +01:00
|
|
|
|
/* Copyright (c) Cloud Software Group, Inc.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
*
|
|
|
|
|
* 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;
|
2023-04-12 09:31:05 +02:00
|
|
|
|
using System.Collections.Specialized;
|
2022-01-11 13:53:48 +01:00
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.IO;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using System.Linq;
|
2022-01-11 13:53:48 +01:00
|
|
|
|
using System.Reflection;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Text.RegularExpressions;
|
2023-04-12 09:31:05 +02:00
|
|
|
|
using System.Web;
|
2022-01-11 13:53:48 +01:00
|
|
|
|
using System.Xml;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
using XenAdmin.Network;
|
|
|
|
|
using XenAPI;
|
2017-11-17 02:04:45 +01:00
|
|
|
|
using XenCenterLib;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace XenAdmin.Core
|
|
|
|
|
{
|
2022-05-20 19:55:23 +02:00
|
|
|
|
public static partial class Helpers
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
2015-09-07 18:45:57 +02:00
|
|
|
|
|
2015-08-25 15:50:41 +02:00
|
|
|
|
private const long XLVHD_DEF_ALLOCATION_QUANTUM_DIVISOR = 10000;
|
2015-09-07 18:45:57 +02:00
|
|
|
|
public const long XLVHD_MIN_ALLOCATION_QUANTUM_DIVISOR = 50000;
|
|
|
|
|
public const long XLVHD_MAX_ALLOCATION_QUANTUM_DIVISOR = 4000;
|
|
|
|
|
public const long XLVHD_MIN_ALLOCATION_QUANTUM = 16777216; // 16 MB
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
public const int DEFAULT_NAME_TRIM_LENGTH = 50;
|
2022-01-11 13:53:48 +01:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
public const string GuiTempObjectPrefix = "__gui__";
|
|
|
|
|
|
2018-04-10 00:46:52 +02:00
|
|
|
|
public const string PRODUCT_BRAND_XCP_NG = "XCP-ng";
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
public static NumberFormatInfo _nfi = new CultureInfo("en-US", false).NumberFormat;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2021-08-31 12:31:16 +02:00
|
|
|
|
/// Return the given host's product version, or the pool coordinator's product version if
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// the host does not have one, or null if none can be found.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="Host">May be null.</param>
|
|
|
|
|
public static string HostProductVersion(Host host)
|
|
|
|
|
{
|
2021-08-31 12:31:16 +02:00
|
|
|
|
return FromHostOrCoordinator(host, h => h.ProductVersion());
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string HostProductVersionText(Host host)
|
|
|
|
|
{
|
2021-08-31 12:31:16 +02:00
|
|
|
|
return FromHostOrCoordinator(host, h => h.ProductVersionText());
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string HostProductVersionTextShort(Host host)
|
|
|
|
|
{
|
2021-08-31 12:31:16 +02:00
|
|
|
|
return FromHostOrCoordinator(host, h => h.ProductVersionTextShort());
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string HostPlatformVersion(Host host)
|
|
|
|
|
{
|
|
|
|
|
if (host == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
2017-09-03 04:33:29 +02:00
|
|
|
|
return host.PlatformVersion();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-02-28 22:12:09 +01:00
|
|
|
|
private static string FromHostOrCoordinator(Host host, Func<Host, string> fn)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
if (host == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
string output = fn(host);
|
|
|
|
|
|
|
|
|
|
if (output == null)
|
|
|
|
|
{
|
2021-08-31 12:31:16 +02:00
|
|
|
|
Host coordinator = GetCoordinator(host.Connection);
|
|
|
|
|
return coordinator == null ? null : fn(coordinator);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2013-09-18 14:06:47 +02:00
|
|
|
|
/// Gets the pool for the provided connection. Returns null if we have
|
|
|
|
|
/// a standalone host (or the provided connection is null).
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// </summary>
|
2013-09-18 14:06:47 +02:00
|
|
|
|
/// <remarks> To obtain the pool object for the case of a standalone host
|
|
|
|
|
/// use GetPoolOfOne.</remarks>
|
2013-06-24 13:41:48 +02:00
|
|
|
|
public static Pool GetPool(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
if (connection == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
2013-09-18 14:06:47 +02:00
|
|
|
|
foreach (Pool thePool in connection.Cache.Pools)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2017-09-03 04:33:29 +02:00
|
|
|
|
if (thePool != null && thePool.IsVisible())
|
2013-09-18 14:06:47 +02:00
|
|
|
|
return thePool;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2013-09-18 14:06:47 +02:00
|
|
|
|
/// Get the unique Pool object corresponding to the given connection.
|
|
|
|
|
/// Returns the pool object even in the case of a standalone host. May
|
|
|
|
|
/// return null if the cache is still being populated or the given
|
|
|
|
|
/// connection is null.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
public static Pool GetPoolOfOne(IXenConnection connection)
|
|
|
|
|
{
|
2021-10-04 16:09:42 +02:00
|
|
|
|
return connection?.Cache.Pools.FirstOrDefault();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2021-08-31 12:31:16 +02:00
|
|
|
|
/// Return the host object representing the coordinator of the given connection, or null if the
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// cache is being populated.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="connection">May not be null.</param>
|
|
|
|
|
/// <returns></returns>
|
2021-08-31 12:31:16 +02:00
|
|
|
|
public static Host GetCoordinator(IXenConnection connection)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
Pool pool = GetPoolOfOne(connection);
|
|
|
|
|
return pool == null ? null : connection.Resolve(pool.master);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2021-08-31 12:31:16 +02:00
|
|
|
|
/// Return the host object representing the coordinator of the given pool.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// (If pool is null, returns null).
|
|
|
|
|
/// </summary>
|
2021-08-31 12:31:16 +02:00
|
|
|
|
public static Host GetCoordinator(Pool pool)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
return pool == null ? null : pool.Connection.Resolve(pool.master);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-31 12:31:16 +02:00
|
|
|
|
public static bool HostIsCoordinator(Host host)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
Pool pool = Helpers.GetPoolOfOne(host.Connection);
|
|
|
|
|
if (pool == null) //Cache is being populated... what do we do?
|
|
|
|
|
return false;
|
|
|
|
|
return host.opaque_ref == pool.master.opaque_ref;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsPool(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
return (GetPool(connection) != null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <param name="pool">May be null, in which case the empty string is returned.</param>
|
|
|
|
|
public static string GetName(Pool pool)
|
|
|
|
|
{
|
2017-09-03 04:33:29 +02:00
|
|
|
|
return pool == null ? "" : pool.Name();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <param name="connection">May be null, in which case the empty string is returned.</param>
|
|
|
|
|
public static string GetName(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
return connection == null ? "" : connection.Name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <param name="o">May be null, in which case the empty string is returned.</param>
|
|
|
|
|
public static string GetName(IXenObject o)
|
|
|
|
|
{
|
2017-09-03 04:33:29 +02:00
|
|
|
|
return o == null ? "" : o.Name();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool HasFullyConnectedSharedStorage(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
foreach (SR sr in connection.Cache.SRs)
|
|
|
|
|
{
|
2021-04-27 13:50:16 +02:00
|
|
|
|
if (sr.shared && sr.SupportsVdiCreate() && !sr.IsBroken(false) && !sr.IsFull())
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CP-3435: Disable Check for Updates in Common Criteria Certification project
|
|
|
|
|
public static bool CommonCriteriaCertificationRelease
|
|
|
|
|
{
|
|
|
|
|
get { return false; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool WlbEnabled(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
Pool pool = GetPoolOfOne(connection);
|
2019-01-31 00:00:56 +01:00
|
|
|
|
return pool != null && pool.wlb_enabled;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool WlbEnabledAndConfigured(IXenConnection conn)
|
|
|
|
|
{
|
2022-01-11 13:53:48 +01:00
|
|
|
|
return WlbEnabled(conn) && WlbConfigured(conn);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool WlbConfigured(IXenConnection conn)
|
|
|
|
|
{
|
|
|
|
|
Pool p = GetPoolOfOne(conn);
|
2019-01-31 00:00:56 +01:00
|
|
|
|
return p != null && !string.IsNullOrEmpty(p.wlb_url);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
2015-12-16 13:58:24 +01:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Determines whether two lists contain the same elements (but not necessarily in the same order).
|
|
|
|
|
/// Compares list elements using reference equality.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
|
|
/// <param name="lst1">Must not be null.</param>
|
|
|
|
|
/// <param name="lst2">Must not be null.</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static bool ListsContentsEqual<T>(List<T> lst1, List<T> lst2)
|
|
|
|
|
{
|
|
|
|
|
if (lst1.Count != lst2.Count)
|
|
|
|
|
return false;
|
|
|
|
|
foreach (T item1 in lst1)
|
|
|
|
|
if (!lst2.Contains(item1))
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Determines if two arrays have the same elements in the same order.
|
|
|
|
|
/// Elements may be null.
|
|
|
|
|
/// Uses Object.Equals() when comparing (pairs of non-null) elements.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
|
|
/// <param name="a">Must not be null.</param>
|
|
|
|
|
/// <param name="b">Must not be null.</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static bool ArrayElementsEqual<T>(T[] a, T[] b)
|
|
|
|
|
{
|
|
|
|
|
if (a.Length != b.Length)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < a.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
if (a[i] == null && b[i] == null)
|
|
|
|
|
continue;
|
|
|
|
|
if (a[i] == null)
|
|
|
|
|
return false;
|
|
|
|
|
if (!a[i].Equals(b[i]))
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal static bool OrderOfMagnitudeDifference(long MinSize, long MaxSize)
|
|
|
|
|
{
|
|
|
|
|
if (MinSize < Util.BINARY_KILO && MaxSize < Util.BINARY_KILO)
|
|
|
|
|
return false;
|
|
|
|
|
else if (MinSize < Util.BINARY_KILO && MaxSize > Util.BINARY_KILO)
|
|
|
|
|
return true;
|
|
|
|
|
else if (MinSize > Util.BINARY_KILO && MaxSize < Util.BINARY_KILO)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (MinSize < Util.BINARY_MEGA && MaxSize < Util.BINARY_MEGA)
|
|
|
|
|
return false;
|
|
|
|
|
else if (MinSize < Util.BINARY_MEGA && MaxSize > Util.BINARY_MEGA)
|
|
|
|
|
return true;
|
|
|
|
|
else if (MinSize > Util.BINARY_MEGA && MaxSize < Util.BINARY_MEGA)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (MinSize < Util.BINARY_GIGA && MaxSize < Util.BINARY_GIGA)
|
|
|
|
|
return false;
|
|
|
|
|
else if (MinSize < Util.BINARY_GIGA && MaxSize > Util.BINARY_GIGA)
|
|
|
|
|
return true;
|
|
|
|
|
else if (MinSize > Util.BINARY_GIGA && MaxSize < Util.BINARY_GIGA)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string StringFromMaxMinSize(long min, long max)
|
|
|
|
|
{
|
|
|
|
|
if (min == -1 && max == -1)
|
|
|
|
|
return Messages.SIZE_NEGLIGIBLE;
|
|
|
|
|
else if (min == -1)
|
|
|
|
|
return Util.LThanSize(max);
|
|
|
|
|
else if (max == -1)
|
|
|
|
|
return Util.GThanSize(min);
|
|
|
|
|
else if (min == max)
|
|
|
|
|
return Util.DiskSizeString(max);
|
|
|
|
|
else if (Helpers.OrderOfMagnitudeDifference(min, max))
|
|
|
|
|
return string.Format("{0} - {1}", Util.DiskSizeString(min), Util.DiskSizeString(max));
|
|
|
|
|
else
|
|
|
|
|
return string.Format("{0} - {1}", Util.DiskSizeStringWithoutUnits(min), Util.DiskSizeString(max));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string StringFromMaxMinSizeList(List<long> minList, List<long> maxList)
|
|
|
|
|
{
|
|
|
|
|
bool lessFlag = false;
|
|
|
|
|
bool moreFlag = false;
|
|
|
|
|
bool negligFlag = false;
|
|
|
|
|
|
|
|
|
|
long minSum = 0;
|
|
|
|
|
long maxSum = 0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < minList.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (minList[i] < 0 && maxList[i] < 0)
|
|
|
|
|
{
|
|
|
|
|
negligFlag = true;
|
|
|
|
|
}
|
|
|
|
|
else if (minList[i] < 0)
|
|
|
|
|
{
|
|
|
|
|
maxSum += maxList[i];
|
|
|
|
|
lessFlag = true;
|
|
|
|
|
}
|
|
|
|
|
else if (maxList[i] < 0)
|
|
|
|
|
{
|
|
|
|
|
minSum += minList[i];
|
|
|
|
|
moreFlag = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
minSum += minList[i];
|
|
|
|
|
maxSum += maxList[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (moreFlag)
|
|
|
|
|
return Util.GThanSize(minSum);
|
|
|
|
|
if (lessFlag)
|
|
|
|
|
return Util.LThanSize(maxSum);
|
|
|
|
|
if (negligFlag && maxSum <= 0)
|
|
|
|
|
return Util.DiskSizeString(maxSum);
|
|
|
|
|
return StringFromMaxMinSize(minSum, maxSum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string GetCPUProperties(Host_cpu cpu)
|
|
|
|
|
{
|
|
|
|
|
return string.Format("{0}\n{1}\n{2}",
|
|
|
|
|
string.Format(Messages.GENERAL_CPU_VENDOR, cpu.vendor),
|
|
|
|
|
string.Format(Messages.GENERAL_CPU_MODEL, cpu.modelname),
|
|
|
|
|
string.Format(Messages.GENERAL_CPU_SPEED, cpu.speed));
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 15:16:40 +02:00
|
|
|
|
public static string GetAllocationProperties(string initial_allocation, string quantum_allocation)
|
|
|
|
|
{
|
|
|
|
|
return string.Format(Messages.SR_DISK_SPACE_ALLOCATION,
|
2015-09-17 14:24:21 +02:00
|
|
|
|
Util.MemorySizeStringSuitableUnits(Convert.ToDouble(initial_allocation), true, Messages.VAL_MB),
|
|
|
|
|
Util.MemorySizeStringSuitableUnits(Convert.ToDouble(quantum_allocation), true, Messages.VAL_MB));
|
2015-08-21 15:16:40 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
public static string GetHostRestrictions(Host host)
|
|
|
|
|
{
|
|
|
|
|
string output = "";
|
|
|
|
|
|
|
|
|
|
List<string> restrictions = new List<string>();
|
|
|
|
|
|
|
|
|
|
// Build license details info box
|
|
|
|
|
foreach (String key in host.license_params.Keys)
|
|
|
|
|
{
|
|
|
|
|
if (host.license_params[key] == "true")
|
|
|
|
|
{
|
|
|
|
|
restrictions.Add(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool first = true;
|
|
|
|
|
restrictions.Sort();
|
|
|
|
|
foreach (String restriction in restrictions)
|
|
|
|
|
{
|
|
|
|
|
string restrictionText = Messages.ResourceManager.GetString(restriction);
|
|
|
|
|
if (restrictionText == null)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (first)
|
|
|
|
|
{
|
|
|
|
|
output += restrictionText;
|
|
|
|
|
first = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output += "\n" + restrictionText;
|
|
|
|
|
}
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-09 02:13:08 +01:00
|
|
|
|
public static string BoolToString(bool b)
|
|
|
|
|
{
|
|
|
|
|
return b ? Messages.YES : Messages.NO;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
private static readonly Regex IqnRegex = new Regex(@"^iqn\.\d{4}-\d{2}\.([a-zA-Z0-9][-_a-zA-Z0-9]*(\.[a-zA-Z0-9][-_a-zA-Z0-9]*)*)(:.+)?$", RegexOptions.ECMAScript);
|
|
|
|
|
|
|
|
|
|
public static bool ValidateIscsiIQN(string iqn)
|
|
|
|
|
{
|
|
|
|
|
return IqnRegex.IsMatch(iqn);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-31 12:31:16 +02:00
|
|
|
|
public static bool IsOlderThanCoordinator(Host host)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2023-07-03 21:50:12 +02:00
|
|
|
|
Host coordinator = GetCoordinator(host.Connection);
|
|
|
|
|
|
2021-08-31 12:31:16 +02:00
|
|
|
|
if (coordinator == null || coordinator.opaque_ref == host.opaque_ref)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return false;
|
2023-07-03 21:50:12 +02:00
|
|
|
|
|
|
|
|
|
return ProductVersionCompare(HostProductVersion(host), HostProductVersion(coordinator)) < 0;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static readonly Regex MacRegex = new Regex(@"^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$");
|
|
|
|
|
|
|
|
|
|
public static bool IsValidMAC(string macString)
|
|
|
|
|
{
|
|
|
|
|
return MacRegex.IsMatch(macString);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Recursively sums the total size of all files in the directory tree rooted at the given dir.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static long GetDirSize(DirectoryInfo dir)
|
|
|
|
|
{
|
|
|
|
|
long size = 0;
|
|
|
|
|
FileInfo[] files = dir.GetFiles();
|
|
|
|
|
foreach (FileInfo file in files)
|
|
|
|
|
{
|
|
|
|
|
size += file.Length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DirectoryInfo[] subdirs = dir.GetDirectories();
|
|
|
|
|
foreach (DirectoryInfo subdir in subdirs)
|
|
|
|
|
{
|
|
|
|
|
size += GetDirSize(subdir);
|
|
|
|
|
}
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string ToWindowsLineEndings(string input)
|
|
|
|
|
{
|
|
|
|
|
return Regex.Replace(input, "\r?\n", "\r\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool PoolHasEnabledHosts(Pool pool)
|
|
|
|
|
{
|
|
|
|
|
foreach (Host host in pool.Connection.Cache.Hosts)
|
|
|
|
|
{
|
|
|
|
|
if (host.enabled)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string MakeUniqueName(string stub, List<string> compareAgainst)
|
|
|
|
|
{
|
|
|
|
|
string pre = stub;
|
|
|
|
|
int i = 1;
|
|
|
|
|
while (compareAgainst.Contains(stub))
|
|
|
|
|
{
|
|
|
|
|
stub = string.Format(Messages.NEWVM_DEFAULTNAME, pre, i);
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
return stub;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string MakeUniqueNameFromPattern(string pattern, List<string> compareAgainst, int startAt)
|
|
|
|
|
{
|
|
|
|
|
int i = startAt;
|
|
|
|
|
string val;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
val = string.Format(pattern, i++);
|
|
|
|
|
} while (compareAgainst.Contains(val));
|
|
|
|
|
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
[CA-233454] PVS tab doesn't show a new VM when it's created (#1314)
* [CA-233454] PVS tab doesn't show a new VM when it's created
Refined the rules for not adding a VM to the table, if it is a template (thus not_a_real_vm), and it has the __gui__ prefix (thus hidden), we still add it, but hide it.
When its name is changed (to remove the __gui__ prefix), we update its name and re-calculate whether it should be visible (in the case of a new VM this will be true once the __gui__ prefix is gone). Also resort the table if a node changes from hidden to visible, because it appears as an addition to the table.
Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com>
* [CA-233454] Update criteria for VM visibility to include is_a_real_vm
is_a_template is changed before the name_label removes the __gui__ prefix, so this works with no other changes to the vm property changed event.
Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com>
* [CA-233454] Update when the VMs are shown in the PVS list
New observable property IsBeingCreated for VMs, set to true when they're made a hidden object, and false when they're removed from hidden objects (both in CreateVMAction). In the PVS Page when this is set to false, we re-evaluate whether a VM can be shown. This means that new VMs show here at the same time they're added to the tree (only different is tree refresh time), instead of far earlier (and before their networks were added).
Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com>
* [CA-233454] Set IsbeingCreated in the CreateVMFastAction
* [CA-233454] Properly support the VM Fast Create action
Further changes to CreateVMFastAction, to ensure it works with PVS tab - use the __gui__ prefix when the VM is created and then change it back just before showing.
* [CA-233454] Small logic adjustments/tidying up
2016-11-30 13:24:02 +01:00
|
|
|
|
public static string MakeHiddenName(string name)
|
|
|
|
|
{
|
|
|
|
|
return string.Format("{0}{1}", GuiTempObjectPrefix, name);
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-05 12:57:16 +02:00
|
|
|
|
public static string GetFriendlyLicenseName(Pool pool)
|
|
|
|
|
{
|
|
|
|
|
var hosts = new List<Host>(pool.Connection.Cache.Hosts);
|
|
|
|
|
|
|
|
|
|
if (hosts.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
var editions = Enum.GetValues(typeof(Host.Edition));
|
|
|
|
|
foreach (Host.Edition edition in editions)
|
|
|
|
|
{
|
|
|
|
|
Host.Edition edition1 = edition;
|
|
|
|
|
Host host = hosts.Find(h => Host.GetEdition(h.edition) == edition1);
|
|
|
|
|
|
|
|
|
|
if (host != null)
|
|
|
|
|
return GetFriendlyLicenseName(host);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Messages.UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
public static string GetFriendlyLicenseName(Host host)
|
|
|
|
|
{
|
2022-01-11 13:53:48 +01:00
|
|
|
|
if (string.IsNullOrEmpty(host.edition))
|
|
|
|
|
return Messages.UNKNOWN;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2018-09-04 11:31:41 +02:00
|
|
|
|
string legacy = NaplesOrGreater(host) ? "" : "legacy-";
|
2019-01-25 01:03:17 +01:00
|
|
|
|
string name = FriendlyNameManager.GetFriendlyName("Label-host.edition-" + legacy + host.edition);
|
2023-01-14 02:08:20 +01:00
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(name))
|
|
|
|
|
return Messages.UNKNOWN;
|
|
|
|
|
|
|
|
|
|
return name.Contains("{0}") ? string.Format(name, BrandManager.ProductBrand) : name;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Used for determining which features are available on the current license.
|
|
|
|
|
/// Note that all the features are per-pool: e.g., even if iXenObject is a Host,
|
|
|
|
|
/// we return whether *any* the hosts in that pool are forbidden that feature.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static bool FeatureForbidden(IXenObject iXenObject, Predicate<Host> restrictionTest)
|
|
|
|
|
{
|
2022-07-12 15:24:17 +02:00
|
|
|
|
return FeatureForbidden(iXenObject?.Connection, restrictionTest);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool FeatureForbidden(IXenConnection xenConnection, Predicate<Host> restrictionTest)
|
|
|
|
|
{
|
|
|
|
|
if (xenConnection == null)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
foreach (Host host in xenConnection.Cache.Hosts)
|
|
|
|
|
{
|
|
|
|
|
if (restrictionTest(host))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Parse string represented double to a double with en-US number format
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="toParse">String represented double</param>
|
|
|
|
|
/// <param name="defaultValue">Default value to use if the string can't be parsed</param>
|
|
|
|
|
/// <returns>The parsed double.</returns>
|
|
|
|
|
public static double ParseStringToDouble(string toParse, double defaultValue)
|
|
|
|
|
{
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (double.TryParse(toParse, NumberStyles.Any, _nfi, out var doubleValue))
|
|
|
|
|
return doubleValue;
|
|
|
|
|
|
|
|
|
|
return defaultValue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Return the UUID of the given XenObject, or the empty string if that can't be found.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static string GetUuid(IXenObject iXenObject)
|
|
|
|
|
{
|
|
|
|
|
if (iXenObject == null)
|
|
|
|
|
return "";
|
|
|
|
|
Type t = iXenObject.GetType();
|
|
|
|
|
PropertyInfo p = t.GetProperty("uuid", BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
|
if (p == null)
|
|
|
|
|
return "";
|
|
|
|
|
return (string)p.GetValue(iXenObject, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Reflexive other_config and gui_config functions
|
|
|
|
|
|
|
|
|
|
public static Dictionary<String, String> GetOtherConfig(IXenObject o)
|
|
|
|
|
{
|
|
|
|
|
return o.Get("other_config") as Dictionary<String, String>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void SetOtherConfig(Session session, IXenObject o, String key, String value)
|
|
|
|
|
{
|
|
|
|
|
//Program.AssertOffEventThread();
|
|
|
|
|
|
|
|
|
|
o.Do("remove_from_other_config", session, o.opaque_ref, key);
|
|
|
|
|
o.Do("add_to_other_config", session, o.opaque_ref, key, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void RemoveFromOtherConfig(Session session, IXenObject o, string key)
|
|
|
|
|
{
|
|
|
|
|
//Program.AssertOffEventThread();
|
|
|
|
|
|
|
|
|
|
o.Do("remove_from_other_config", session, o.opaque_ref, key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Dictionary<String, String> GetGuiConfig(IXenObject o)
|
|
|
|
|
{
|
|
|
|
|
return o.Get("gui_config") as Dictionary<String, String>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2021-07-31 17:58:30 +02:00
|
|
|
|
public enum DataSourceCategory
|
|
|
|
|
{
|
|
|
|
|
Cpu,
|
|
|
|
|
Memory,
|
|
|
|
|
Disk,
|
|
|
|
|
Storage,
|
|
|
|
|
Network,
|
|
|
|
|
Latency,
|
|
|
|
|
LoadAverage,
|
|
|
|
|
Gpu,
|
|
|
|
|
Pvs,
|
|
|
|
|
Custom
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-07-31 17:58:30 +02:00
|
|
|
|
public static string ToStringI18N(this DataSourceCategory category)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2021-07-31 17:58:30 +02:00
|
|
|
|
switch (category)
|
|
|
|
|
{
|
|
|
|
|
case DataSourceCategory.Cpu:
|
|
|
|
|
return Messages.DATATYPE_CPU;
|
|
|
|
|
case DataSourceCategory.Memory:
|
|
|
|
|
return Messages.DATATYPE_MEMORY;
|
|
|
|
|
case DataSourceCategory.Disk:
|
|
|
|
|
return Messages.DATATYPE_DISK;
|
|
|
|
|
case DataSourceCategory.Storage:
|
|
|
|
|
return Messages.DATATYPE_STORAGE;
|
|
|
|
|
case DataSourceCategory.Network:
|
|
|
|
|
return Messages.DATATYPE_NETWORK;
|
|
|
|
|
case DataSourceCategory.Latency:
|
|
|
|
|
return Messages.DATATYPE_LATENCY;
|
|
|
|
|
case DataSourceCategory.LoadAverage:
|
|
|
|
|
return Messages.DATATYPE_LOADAVERAGE;
|
|
|
|
|
case DataSourceCategory.Gpu:
|
|
|
|
|
return Messages.DATATYPE_GPU;
|
|
|
|
|
case DataSourceCategory.Pvs:
|
|
|
|
|
return Messages.DATATYPE_PVS;
|
|
|
|
|
default:
|
|
|
|
|
return Messages.DATATYPE_CUSTOM;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Regex CpuRegex = new Regex("^cpu([0-9]+)$");
|
2021-07-31 17:58:30 +02:00
|
|
|
|
static Regex CpuAvgFreqRegex = new Regex("^CPU([0-9]+)-avg-freq$");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
public static Regex CpuStateRegex = new Regex("^cpu([0-9]+)-(C|P)([0-9]+)$");
|
2023-10-03 12:01:42 +02:00
|
|
|
|
static Regex CpuOtherRegex = new Regex("^cpu_avg$");
|
2021-07-31 17:58:30 +02:00
|
|
|
|
private static Regex VcpuRegex = new Regex("^runstate_(blocked|concurrency_hazard|full_contention|fullrun|partial_contention|partial_run)$");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
static Regex VifRegex = new Regex("^vif_([0-9]+)_(tx|rx)((_errors)?)$");
|
|
|
|
|
static Regex PifEthRegex = new Regex("^pif_eth([0-9]+)_(tx|rx)((_errors)?)$");
|
2021-07-31 17:58:30 +02:00
|
|
|
|
static Regex PifVlanRegex = new Regex("^pif_eth([0-9]+).([0-9]+)_(tx|rx)((_errors)?)$");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
static Regex PifBrRegex = new Regex("^pif_xenbr([0-9]+)_(tx|rx)((_errors)?)$");
|
2021-07-31 17:58:30 +02:00
|
|
|
|
static Regex PifXapiRegex = new Regex("^pif_xapi([0-9]+)_(tx|rx)((_errors)?)$");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
static Regex PifTapRegex = new Regex("^pif_tap([0-9]+)_(tx|rx)((_errors)?)$");
|
|
|
|
|
static Regex PifLoRegex = new Regex("^pif_lo_(tx|rx)((_errors)?)$");
|
|
|
|
|
static Regex PifBondRegex = new Regex("^pif_(bond[0-9]+)_(tx|rx)((_errors)?)$");
|
2021-07-31 17:58:30 +02:00
|
|
|
|
static Regex PifOtherRegex = new Regex("^pif_aggr_(tx|rx)$");
|
2020-05-06 17:23:14 +02:00
|
|
|
|
static Regex DiskRegex = new Regex("^vbd_((xvd|hd)[a-z]+)(_(read|write))?(_latency)?$");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
static Regex DiskIopsRegex = new Regex("^vbd_((xvd|hd)[a-z]+)_iops_(read|write|total)$");
|
2015-04-23 18:38:40 +02:00
|
|
|
|
static Regex DiskThroughputRegex = new Regex("^vbd_((xvd|hd)[a-z]+)_io_throughput_(read|write|total)$");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
static Regex DiskOtherRegex = new Regex("^vbd_((xvd|hd)[a-z]+)_(avgqu_sz|inflight|iowait)$");
|
|
|
|
|
static Regex NetworkLatencyRegex = new Regex("^network/latency$");
|
|
|
|
|
static Regex XapiLatencyRegex = new Regex("^xapi_healthcheck/latency$");
|
2021-07-31 17:58:30 +02:00
|
|
|
|
static Regex XapiMemoryRegex = new Regex("^xapi_(allocation|free_memory|live_memory|memory_usage)_kib$");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
static Regex StatefileLatencyRegex = new Regex("^statefile/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/latency$");
|
|
|
|
|
static Regex LoadAvgRegex = new Regex("loadavg");
|
2023-10-24 15:50:08 +02:00
|
|
|
|
static Regex HostLoadRegex = new Regex("hostload");
|
2021-07-31 17:58:30 +02:00
|
|
|
|
static Regex SrRegex = new Regex("^sr_[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}_cache_(size|hits|misses)");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
static Regex SrIORegex = new Regex("^(io_throughput|iops)_(read|write|total)_([a-f0-9]{8})$");
|
|
|
|
|
static Regex SrOtherRegex = new Regex("^(latency|avgqu_sz|inflight|iowait)_([a-f0-9]{8})$");
|
2016-10-26 17:03:12 +02:00
|
|
|
|
static Regex SrReadWriteRegex = new Regex("^((read|write)(_latency)?)_([a-f0-9]{8})$");
|
2019-09-07 02:22:49 +02:00
|
|
|
|
static Regex GpuRegex = new Regex(@"^gpu_((memory_(free|used))|power_usage|temperature|(utilisation_(compute|memory_io)))_((([a-fA-F0-9]{4}\/)|([a-fA-F0-9]{8}\/))?[a-fA-F0-9]{2}\/[0-1][a-fA-F0-9].[0-7])$");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2021-07-31 17:58:30 +02:00
|
|
|
|
public static DataSourceCategory GetDataSourceCategory(string name)
|
|
|
|
|
{
|
|
|
|
|
if (CpuRegex.IsMatch(name) || CpuAvgFreqRegex.IsMatch(name) ||
|
|
|
|
|
CpuStateRegex.IsMatch(name) || CpuOtherRegex.IsMatch(name) || VcpuRegex.IsMatch(name))
|
|
|
|
|
return DataSourceCategory.Cpu;
|
|
|
|
|
|
|
|
|
|
if (VifRegex.IsMatch(name) || PifEthRegex.IsMatch(name) || PifVlanRegex.IsMatch(name) ||
|
|
|
|
|
PifBrRegex.IsMatch(name) || PifXapiRegex.IsMatch(name) || PifBondRegex.IsMatch(name) ||
|
|
|
|
|
PifLoRegex.IsMatch(name) || PifTapRegex.IsMatch(name) || PifOtherRegex.IsMatch(name))
|
|
|
|
|
return DataSourceCategory.Network;
|
|
|
|
|
|
|
|
|
|
if (DiskRegex.IsMatch(name) || DiskIopsRegex.IsMatch(name) ||
|
|
|
|
|
DiskThroughputRegex.IsMatch(name) || DiskOtherRegex.IsMatch(name))
|
|
|
|
|
return DataSourceCategory.Disk;
|
|
|
|
|
|
|
|
|
|
if (SrRegex.IsMatch(name) || SrIORegex.IsMatch(name) ||
|
|
|
|
|
SrOtherRegex.IsMatch(name) || SrReadWriteRegex.IsMatch(name))
|
|
|
|
|
return DataSourceCategory.Storage;
|
|
|
|
|
|
|
|
|
|
if (GpuRegex.IsMatch(name))
|
|
|
|
|
return DataSourceCategory.Gpu;
|
|
|
|
|
|
|
|
|
|
if (NetworkLatencyRegex.IsMatch(name) || XapiLatencyRegex.IsMatch(name) ||
|
|
|
|
|
StatefileLatencyRegex.IsMatch(name))
|
|
|
|
|
return DataSourceCategory.Latency;
|
|
|
|
|
|
|
|
|
|
if (LoadAvgRegex.IsMatch(name))
|
|
|
|
|
return DataSourceCategory.LoadAverage;
|
|
|
|
|
|
2023-10-24 15:50:08 +02:00
|
|
|
|
if (HostLoadRegex.IsMatch(name))
|
|
|
|
|
return DataSourceCategory.LoadAverage;
|
|
|
|
|
|
2021-07-31 17:58:30 +02:00
|
|
|
|
if (name.StartsWith("pvsaccelerator"))
|
|
|
|
|
return DataSourceCategory.Pvs;
|
|
|
|
|
|
|
|
|
|
if (XapiMemoryRegex.IsMatch(name) || name.StartsWith("memory"))
|
|
|
|
|
return DataSourceCategory.Memory;
|
|
|
|
|
|
|
|
|
|
return DataSourceCategory.Custom;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
public static string GetFriendlyDataSourceName(string name, IXenObject iXenObject)
|
|
|
|
|
{
|
|
|
|
|
if (iXenObject == null)
|
|
|
|
|
return name;
|
|
|
|
|
string s = GetFriendlyDataSourceName_(name, iXenObject);
|
|
|
|
|
return string.IsNullOrEmpty(s) ? name : s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string GetFriendlyDataSourceName_(string name, IXenObject iXenObject)
|
|
|
|
|
{
|
|
|
|
|
Match m;
|
|
|
|
|
|
|
|
|
|
m = CpuRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
return FormatFriendly("Label-performance.cpu", m.Groups[1].Value);
|
|
|
|
|
|
2018-04-23 15:29:44 +02:00
|
|
|
|
m = CpuAvgFreqRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
return FormatFriendly("Label-performance.cpu-avg-freq", m.Groups[1].Value);
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
m = VifRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
string device = m.Groups[1].Value;
|
|
|
|
|
XenAPI.Network network = FindNetworkOfVIF(iXenObject, device);
|
|
|
|
|
return network == null
|
|
|
|
|
? null //don't try to retrieve it in the FriendlyNames.
|
|
|
|
|
: FormatFriendly(string.Format("Label-performance.vif_{0}{1}",
|
2017-09-03 04:33:29 +02:00
|
|
|
|
m.Groups[2].Value, m.Groups[3].Value), network.Name());
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m = PifEthRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
return FormatFriendly(string.Format("Label-performance.nic_{0}{1}", m.Groups[2].Value, m.Groups[3].Value), m.Groups[1].Value);
|
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
m = PifVlanRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
string device = string.Format("eth{0}", m.Groups[1].Value);
|
|
|
|
|
XenAPI.Network network = FindVlan(iXenObject, device, m.Groups[2].Value);
|
|
|
|
|
return network == null
|
|
|
|
|
? null //don't try to retrieve it in the FriendlyNames.
|
|
|
|
|
: FormatFriendly(string.Format("Label-performance.vlan_{0}{1}",
|
|
|
|
|
m.Groups[3].Value, m.Groups[4].Value), network.Name());
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
m = PifBrRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
string device = string.Format("eth{0}", m.Groups[1].Value);
|
|
|
|
|
XenAPI.Network network = FindNetworkOfPIF(iXenObject, device);
|
2022-01-11 13:53:48 +01:00
|
|
|
|
return network == null
|
|
|
|
|
? null //don't try to retrieve it in the FriendlyNames.
|
|
|
|
|
: FormatFriendly(string.Format("Label-performance.xenbr_{0}{1}", m.Groups[2].Value, m.Groups[3].Value), network.Name());
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
m = PifXapiRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
return FormatFriendly(string.Format("Label-performance.xapi_{0}{1}", m.Groups[2].Value, m.Groups[3].Value), m.Groups[1].Value);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
m = PifBondRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
2022-01-11 13:53:48 +01:00
|
|
|
|
PIF pif = FindPIF(iXenObject, m.Groups[1].Value, false);
|
|
|
|
|
return pif == null
|
|
|
|
|
? null //pif doesn't exist anymore so don't try to retrieve it in the FriendlyNames.
|
|
|
|
|
: FormatFriendly(string.Format("Label-performance.bond_{0}{1}", m.Groups[2].Value, m.Groups[3].Value), pif.Name());
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
m = PifLoRegex.Match(name);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (m.Success)
|
|
|
|
|
return FormatFriendly(string.Format("Label-performance.lo_{0}{1}", m.Groups[1].Value, m.Groups[2].Value));
|
|
|
|
|
|
|
|
|
|
m = PifTapRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
return FormatFriendly(string.Format("Label-performance.tap_{0}{1}", m.Groups[2].Value, m.Groups[3].Value), m.Groups[1].Value);
|
|
|
|
|
|
|
|
|
|
m = DiskRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
VBD vbd = FindVBD(iXenObject, m.Groups[1].Value);
|
|
|
|
|
return vbd == null
|
|
|
|
|
? null
|
2020-05-06 17:23:14 +02:00
|
|
|
|
: FormatFriendly(string.Format("Label-performance.vbd{0}{1}", m.Groups[3].Value, m.Groups[5].Value), vbd.userdevice);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m = DiskIopsRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
VBD vbd = FindVBD(iXenObject, m.Groups[1].Value);
|
|
|
|
|
return vbd == null
|
|
|
|
|
? null
|
|
|
|
|
: FormatFriendly(string.Format("Label-performance.vbd_iops_{0}", m.Groups[3].Value),
|
|
|
|
|
vbd.userdevice);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-23 18:38:40 +02:00
|
|
|
|
m = DiskThroughputRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
VBD vbd = FindVBD(iXenObject, m.Groups[1].Value);
|
|
|
|
|
return vbd == null
|
|
|
|
|
? null
|
|
|
|
|
: FormatFriendly(string.Format("Label-performance.vbd_io_throughput_{0}", m.Groups[3].Value),
|
|
|
|
|
vbd.userdevice);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
m = DiskOtherRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
VBD vbd = FindVBD(iXenObject, m.Groups[1].Value);
|
|
|
|
|
return vbd == null
|
|
|
|
|
? null
|
|
|
|
|
: FormatFriendly(string.Format("Label-performance.vbd_{0}", m.Groups[3].Value),
|
|
|
|
|
vbd.userdevice);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
m = SrRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
return FormatFriendly(string.Format("Label-performance.sr_cache_{0}", m.Groups[1].Value));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
m = CpuStateRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
return FormatFriendly("Label-performance.cpu-state", m.Groups[1].Value, m.Groups[2].Value, m.Groups[3].Value);
|
|
|
|
|
|
|
|
|
|
m = SrIORegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
SR sr = FindSr(iXenObject, m.Groups[3].Value);
|
|
|
|
|
return sr == null
|
|
|
|
|
? null
|
|
|
|
|
: FormatFriendly(string.Format("Label-performance.sr_{0}_{1}",
|
|
|
|
|
m.Groups[1].Value, m.Groups[2].Value),
|
2017-09-03 04:33:29 +02:00
|
|
|
|
sr.Name().Ellipsise(30));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m = SrOtherRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
SR sr = FindSr(iXenObject, m.Groups[2].Value);
|
|
|
|
|
return sr == null
|
|
|
|
|
? null
|
|
|
|
|
: FormatFriendly(string.Format("Label-performance.sr_{0}", m.Groups[1].Value),
|
2017-09-03 04:33:29 +02:00
|
|
|
|
sr.Name().Ellipsise(30));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2016-10-26 17:03:12 +02:00
|
|
|
|
m = SrReadWriteRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
SR sr = FindSr(iXenObject, m.Groups[4].Value);
|
|
|
|
|
return sr == null
|
|
|
|
|
? null
|
|
|
|
|
: FormatFriendly(string.Format("Label-performance.sr_rw_{0}", m.Groups[1].Value),
|
2017-09-03 04:33:29 +02:00
|
|
|
|
sr.Name().Ellipsise(30));
|
2016-10-26 17:03:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-14 12:33:59 +01:00
|
|
|
|
m = GpuRegex.Match(name);
|
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
string pciId = m.Groups[6].Value.Replace(@"/", ":");
|
|
|
|
|
PGPU gpu = FindGpu(iXenObject, pciId);
|
2019-09-07 02:22:49 +02:00
|
|
|
|
|
|
|
|
|
if (gpu == null && string.IsNullOrEmpty(m.Groups[8].Value))
|
|
|
|
|
{
|
|
|
|
|
pciId = pciId.Substring(4);
|
|
|
|
|
gpu = FindGpu(iXenObject, pciId);
|
|
|
|
|
}
|
2013-11-14 12:33:59 +01:00
|
|
|
|
return gpu == null
|
2019-09-07 02:22:49 +02:00
|
|
|
|
? null
|
|
|
|
|
: FormatFriendly(string.Format("Label-performance.gpu_{0}", m.Groups[1].Value),
|
|
|
|
|
gpu.Name(), pciId);
|
2013-11-14 12:33:59 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
if (NetworkLatencyRegex.IsMatch(name))
|
2019-01-25 01:03:17 +01:00
|
|
|
|
return FriendlyNameManager.GetFriendlyName("Label-performance.network_latency");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
if (XapiLatencyRegex.IsMatch(name))
|
2019-01-25 01:03:17 +01:00
|
|
|
|
return FriendlyNameManager.GetFriendlyName("Label-performance.xapi_latency");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
if (StatefileLatencyRegex.IsMatch(name))
|
2019-01-25 01:03:17 +01:00
|
|
|
|
return FriendlyNameManager.GetFriendlyName("Label-performance.statefile_latency");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
if (LoadAvgRegex.IsMatch(name))
|
2019-01-25 01:03:17 +01:00
|
|
|
|
return FriendlyNameManager.GetFriendlyName("Label-performance.loadavg");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2023-10-24 15:50:08 +02:00
|
|
|
|
if (HostLoadRegex.IsMatch(name))
|
|
|
|
|
return FriendlyNames.OPERATIONAL_METRICS_HOSTLOAD;
|
|
|
|
|
|
2019-01-25 01:03:17 +01:00
|
|
|
|
return FriendlyNameManager.GetFriendlyName(string.Format("Label-performance.{0}", name));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Lookup key using PropertyManager.GetFriendlyName, and then apply that as a format pattern to the given args.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static string FormatFriendly(string key, params string[] args)
|
|
|
|
|
{
|
2019-01-25 01:03:17 +01:00
|
|
|
|
return string.Format(FriendlyNameManager.GetFriendlyName(key), args);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-14 12:33:59 +01:00
|
|
|
|
private static PGPU FindGpu(IXenObject iXenObject, string pciId)
|
|
|
|
|
{
|
|
|
|
|
foreach (PCI pci in iXenObject.Connection.Cache.PCIs)
|
|
|
|
|
{
|
|
|
|
|
if (pci.pci_id != pciId)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
foreach (PGPU gpu in iXenObject.Connection.Cache.PGPUs)
|
|
|
|
|
{
|
|
|
|
|
if (gpu.PCI.opaque_ref == pci.opaque_ref)
|
|
|
|
|
return gpu;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
private static XenAPI.Network FindNetworkOfVIF(IXenObject iXenObject, string device)
|
|
|
|
|
{
|
|
|
|
|
foreach (VIF vif in iXenObject.Connection.Cache.VIFs)
|
|
|
|
|
{
|
|
|
|
|
if (vif.device == device && (iXenObject is Host && ((Host)iXenObject).resident_VMs.Contains(vif.VM) || iXenObject is VM && vif.VM.opaque_ref == iXenObject.opaque_ref))
|
|
|
|
|
{
|
|
|
|
|
XenAPI.Network network = iXenObject.Connection.Resolve(vif.network);
|
|
|
|
|
if (network != null)
|
|
|
|
|
return network;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static XenAPI.Network FindNetworkOfPIF(IXenObject iXenObject, string device)
|
|
|
|
|
{
|
|
|
|
|
PIF pif = FindPIF(iXenObject, device, true);
|
|
|
|
|
if (pif != null)
|
|
|
|
|
{
|
|
|
|
|
XenAPI.Network network = iXenObject.Connection.Resolve(pif.network);
|
|
|
|
|
if (network != null)
|
|
|
|
|
return network;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
private static XenAPI.Network FindVlan(IXenObject iXenObject, string device, string tag)
|
|
|
|
|
{
|
|
|
|
|
foreach (PIF pif in iXenObject.Connection.Cache.PIFs)
|
|
|
|
|
{
|
|
|
|
|
if (pif.device == device && (iXenObject is Host && pif.host.opaque_ref == iXenObject.opaque_ref || iXenObject is VM && pif.host.opaque_ref == ((VM)iXenObject).resident_on.opaque_ref) && pif.VLAN == long.Parse(tag))
|
|
|
|
|
{
|
|
|
|
|
return iXenObject.Connection.Resolve(pif.network);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
private static PIF FindPIF(IXenObject iXenObject, string device, bool physical)
|
|
|
|
|
{
|
|
|
|
|
foreach (PIF pif in iXenObject.Connection.Cache.PIFs)
|
|
|
|
|
{
|
2017-09-03 04:33:29 +02:00
|
|
|
|
if ((!physical || pif.IsPhysical()) && pif.device == device && (iXenObject is Host && pif.host.opaque_ref == iXenObject.opaque_ref || iXenObject is VM && pif.host.opaque_ref == ((VM)iXenObject).resident_on.opaque_ref))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return pif;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static VBD FindVBD(IXenObject iXenObject, string device)
|
|
|
|
|
{
|
2020-05-06 17:23:14 +02:00
|
|
|
|
if (iXenObject is VM vm)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-05-06 17:23:14 +02:00
|
|
|
|
foreach (var vbdRef in vm.VBDs)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-05-06 17:23:14 +02:00
|
|
|
|
var vbd = vm.Connection.Resolve(vbdRef);
|
|
|
|
|
if (vbd != null && vbd.device == device)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return vbd;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static SR FindSr(IXenObject iXenObject, string srUuid)
|
|
|
|
|
{
|
|
|
|
|
foreach (var sr in iXenObject.Connection.Cache.SRs)
|
|
|
|
|
{
|
|
|
|
|
if (sr.uuid.StartsWith(srUuid))
|
|
|
|
|
return sr;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns the first line of the given string, or the empty string if null was passed in. Will take the first occurence of \r or \n
|
|
|
|
|
/// as the end of a line.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="s"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static string FirstLine(string s)
|
|
|
|
|
{
|
2018-08-06 12:01:29 +02:00
|
|
|
|
if (string.IsNullOrEmpty(s))
|
|
|
|
|
return string.Empty;
|
|
|
|
|
|
|
|
|
|
s = s.Split('\n')[0];
|
|
|
|
|
return s.Split('\r')[0];
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static double StringToDouble(string str)
|
|
|
|
|
{
|
|
|
|
|
if (str == "NaN")
|
|
|
|
|
return Double.NaN;
|
|
|
|
|
else if (str == "Infinity")
|
|
|
|
|
return Double.PositiveInfinity;
|
|
|
|
|
else if (str == "-Infinity")
|
|
|
|
|
return Double.NegativeInfinity;
|
|
|
|
|
else
|
|
|
|
|
return Convert.ToDouble(str, CultureInfo.InvariantCulture);
|
|
|
|
|
}
|
2022-01-11 13:53:48 +01:00
|
|
|
|
|
2013-06-24 13:41:48 +02:00
|
|
|
|
public static bool HAEnabled(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
Pool pool = Helpers.GetPoolOfOne(connection);
|
|
|
|
|
if (pool == null)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return pool.ha_enabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns "type 'name'" for the specified object, e.g. "pool 'foo'".
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="XenObject"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static string GetNameAndObject(IXenObject XenObject)
|
|
|
|
|
{
|
|
|
|
|
if (XenObject is Pool)
|
2016-07-04 12:58:59 +02:00
|
|
|
|
return string.Format(Messages.POOL_X, GetName(XenObject));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
if (XenObject is Host)
|
2016-07-04 12:58:59 +02:00
|
|
|
|
return string.Format(Messages.SERVER_X, GetName(XenObject));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2021-02-24 16:44:38 +01:00
|
|
|
|
if (XenObject is VM vm)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2021-02-24 16:44:38 +01:00
|
|
|
|
if (vm.IsControlDomainZero(out var host))
|
|
|
|
|
return string.Format(Messages.SERVER_X, GetName(host));
|
2022-01-11 13:53:48 +01:00
|
|
|
|
|
2021-02-24 16:44:38 +01:00
|
|
|
|
return string.Format(Messages.VM_X, GetName(XenObject));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (XenObject is SR)
|
2016-07-04 12:58:59 +02:00
|
|
|
|
return string.Format(Messages.STORAGE_REPOSITORY_X, GetName(XenObject));
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
return Messages.UNKNOWN_OBJECT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the i18n'd name for a HA restart priority according to FriendlyNames.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="priority"></param>
|
|
|
|
|
/// <returns></returns>
|
2023-09-29 04:45:17 +02:00
|
|
|
|
public static string RestartPriorityI18n(VM.HaRestartPriority? priority)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
if (priority == null)
|
|
|
|
|
{
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-01-25 01:03:17 +01:00
|
|
|
|
return FriendlyNameManager.GetFriendlyName("Label-VM.ha_restart_priority." + priority.ToString()) ?? priority.ToString();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-29 04:45:17 +02:00
|
|
|
|
public static string RestartPriorityDescription(VM.HaRestartPriority? priority)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
if (priority == null)
|
|
|
|
|
{
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-01-25 01:03:17 +01:00
|
|
|
|
return FriendlyNameManager.GetFriendlyName("Description-VM.ha_restart_priority." + priority.ToString()) ?? priority.ToString();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Builds up a dictionary of the current restart priorities for all the VMs in the given IXenConnection.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="connection">Must not be null.</param>
|
|
|
|
|
/// <returns></returns>
|
2023-09-29 04:45:17 +02:00
|
|
|
|
public static Dictionary<VM, VM.HaRestartPriority> GetVmHaRestartPriorities(IXenConnection connection, bool showHiddenVMs)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2023-09-29 04:45:17 +02:00
|
|
|
|
Dictionary<VM, VM.HaRestartPriority> result = new Dictionary<VM, VM.HaRestartPriority>();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
foreach (VM vm in connection.Cache.VMs)
|
|
|
|
|
{
|
|
|
|
|
if (vm.HaCanProtect(showHiddenVMs))
|
|
|
|
|
{
|
2017-09-05 03:15:38 +02:00
|
|
|
|
result[vm] = vm.HARestartPriority();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts Dictionary<XenObject<VM>, VM.HA_Restart_Priority> -> Dictionary<XenRef<VM>, string>.
|
|
|
|
|
/// The former is useful in the GUI, the latter is suitable for sending into compute_hypothetical_max.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="settings">Must not be null.</param>
|
|
|
|
|
/// <returns></returns>
|
2023-09-29 04:45:17 +02:00
|
|
|
|
public static Dictionary<XenRef<VM>, string> GetVmHaRestartPrioritiesForApi(Dictionary<VM, VM.HaRestartPriority> settings)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
Dictionary<XenRef<VM>, string> result = new Dictionary<XenRef<VM>, string>();
|
|
|
|
|
foreach (VM vm in settings.Keys)
|
|
|
|
|
{
|
2023-09-29 04:45:17 +02:00
|
|
|
|
if (settings[vm] == VM.HaRestartPriority.BestEffort || settings[vm] == VM.HaRestartPriority.DoNotRestart)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
|
|
|
|
// The server doesn't want to know about best-effort/do not restart VMs.
|
|
|
|
|
// (They don't influence the plan, and sending in the dictionary gives an error).
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
result[new XenRef<VM>(vm.opaque_ref)] = VM.PriorityToString(settings[vm]);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Builds up a dictionary of the current startup options for all the VMs in the given IXenConnection.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="connection">Must not be null.</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static Dictionary<VM, VMStartupOptions> GetVmStartupOptions(IXenConnection connection, bool showHiddenVMs)
|
|
|
|
|
{
|
|
|
|
|
Dictionary<VM, VMStartupOptions> result = new Dictionary<VM, VMStartupOptions>();
|
|
|
|
|
foreach (VM vm in connection.Cache.VMs)
|
|
|
|
|
{
|
|
|
|
|
if (vm.HaCanProtect(showHiddenVMs))
|
|
|
|
|
{
|
2017-09-05 03:15:38 +02:00
|
|
|
|
result[vm] = new VMStartupOptions(vm.order, vm.start_delay, vm.HARestartPriority());
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Regex HostnameOrIpRegex = new Regex(@"[\w.]+");
|
|
|
|
|
|
|
|
|
|
public static string HostnameFromLocation(string p)
|
|
|
|
|
{
|
2021-10-04 16:09:42 +02:00
|
|
|
|
var matches = HostnameOrIpRegex.Matches(p);
|
|
|
|
|
// we only want the hostname or ip which should be the first match
|
|
|
|
|
return matches.Count > 0 ? matches[0].Value : string.Empty;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
/// Retrieves a float value from an XML attribute.
|
|
|
|
|
/// Returns defaultValue if the attribute doesn't exist.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// </summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
public static string GetStringXmlAttribute(XmlNode node, string attributeName, string defaultValue = null)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (node == null || node.Attributes == null || node.Attributes[attributeName] == null)
|
|
|
|
|
return defaultValue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-09-26 13:36:47 +02:00
|
|
|
|
return node.Attributes[attributeName].Value;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
/// Retrieves a true of false value from an XML attribute.
|
|
|
|
|
/// Returns defaultValue if the attribute doesn't exist or the value is malformed.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// </summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
public static bool GetBoolXmlAttribute(XmlNode node, string attributeName, bool defaultValue = false)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (node == null || node.Attributes == null || node.Attributes[attributeName] == null)
|
|
|
|
|
return defaultValue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (bool.TryParse(node.Attributes[attributeName].Value, out bool b))
|
|
|
|
|
return b;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-09-26 13:36:47 +02:00
|
|
|
|
return defaultValue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
/// Retrieves a float value from an XML attribute.
|
|
|
|
|
/// Returns defaultValue if the attribute doesn't exist or the value is malformed.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// </summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
public static float GetFloatXmlAttribute(XmlNode node, string attributeName, float defaultValue)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (node == null || node.Attributes == null || node.Attributes[attributeName] == null)
|
|
|
|
|
return defaultValue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (float.TryParse(node.Attributes[attributeName].Value, out float f))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return f;
|
|
|
|
|
|
2019-09-26 13:36:47 +02:00
|
|
|
|
return defaultValue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
/// Retrieves an int value from an XML attribute.
|
|
|
|
|
/// Returns defaultValue if the attribute doesn't exist or the value is malformed.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// </summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
public static int GetIntXmlAttribute(XmlNode node, string attributeName, int defaultValue)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (node == null || node.Attributes == null || node.Attributes[attributeName] == null)
|
|
|
|
|
return defaultValue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (int.TryParse(node.Attributes[attributeName].Value, out int i))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
return i;
|
|
|
|
|
|
2019-09-26 13:36:47 +02:00
|
|
|
|
return defaultValue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
/// Retrieves the string content of an XmlNode attribute.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// </summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
/// <exception cref="I18NException">Thrown if the attribute is missing</exception>
|
|
|
|
|
public static string GetXmlAttribute(XmlNode node, string attributeName)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (node == null)
|
|
|
|
|
throw new I18NException(I18NExceptionType.XmlAttributeMissing, attributeName);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (node.Attributes == null || node.Attributes[attributeName] == null)
|
|
|
|
|
throw new I18NException(I18NExceptionType.XmlAttributeMissing, attributeName, node.Name);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-09-26 13:36:47 +02:00
|
|
|
|
return node.Attributes[attributeName].Value;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
/// Retrieves the enum content of an XmlNode attribute or defaultValue if it is missing.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
/// </summary>
|
2019-09-26 13:36:47 +02:00
|
|
|
|
public static T GetEnumXmlAttribute<T>(XmlNode node, string attributeName, T defaultValue)
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (node == null || node.Attributes == null || node.Attributes[attributeName] == null)
|
|
|
|
|
return defaultValue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2019-09-26 13:36:47 +02:00
|
|
|
|
if (!typeof(T).IsEnum)
|
|
|
|
|
return defaultValue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2019-09-26 13:36:47 +02:00
|
|
|
|
return (T)Enum.Parse(typeof(T), node.Attributes[attributeName].Value);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
2019-09-26 13:36:47 +02:00
|
|
|
|
return defaultValue;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string PrettyFingerprint(string p)
|
|
|
|
|
{
|
|
|
|
|
List<string> pairs = new List<string>();
|
|
|
|
|
for (int i = 0; i < p.Length; i += 2)
|
|
|
|
|
{
|
|
|
|
|
pairs.Add(p.Substring(i, 2));
|
|
|
|
|
}
|
|
|
|
|
return string.Join(":", pairs.ToArray());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string GetUrl(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
UriBuilder uriBuilder = new UriBuilder(connection.UriScheme, connection.Hostname);
|
|
|
|
|
uriBuilder.Port = connection.Port;
|
|
|
|
|
return uriBuilder.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string DefaultVMName(string p, IXenConnection connection)
|
|
|
|
|
{
|
2020-05-12 14:27:33 +02:00
|
|
|
|
int i = 0;
|
|
|
|
|
do
|
2013-06-24 13:41:48 +02:00
|
|
|
|
{
|
2020-05-12 14:27:33 +02:00
|
|
|
|
string name = string.Format(Messages.NEWVM_DEFAULTNAME, p, ++i);
|
|
|
|
|
string hiddenName = MakeHiddenName(name);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2020-05-12 14:27:33 +02:00
|
|
|
|
if (connection.Cache.VMs.Any(v => v.name_label == name || v.name_label == hiddenName))
|
2013-06-24 13:41:48 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
2020-05-12 14:27:33 +02:00
|
|
|
|
return name;
|
|
|
|
|
} while (true);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool CompareLists<T>(List<T> l1, List<T> l2)
|
|
|
|
|
{
|
|
|
|
|
if (l1 == l2)
|
|
|
|
|
return true;
|
|
|
|
|
else if (l1 == null || l2 == null)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
bool same = l1.Count == l2.Count;
|
|
|
|
|
foreach (T item in l1)
|
|
|
|
|
{
|
|
|
|
|
if (!l2.Contains(item))
|
|
|
|
|
{
|
|
|
|
|
same = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return same;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool ListsIntersect<T>(List<T> l1, List<T> l2)
|
|
|
|
|
{
|
|
|
|
|
foreach (T item in l1)
|
|
|
|
|
{
|
|
|
|
|
if (l2.Contains(item))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool CustomWithNoDVD(VM template)
|
|
|
|
|
{
|
2017-09-03 04:33:29 +02:00
|
|
|
|
return template != null && !template.DefaultTemplate() && template.FindVMCDROM() == null;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string GetMacString(string mac)
|
|
|
|
|
{
|
|
|
|
|
return mac == "" ? Messages.MAC_AUTOGENERATE : mac;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static PIF FindPIF(XenAPI.Network network, Host owner)
|
|
|
|
|
{
|
|
|
|
|
foreach (PIF pif in network.Connection.ResolveAll(network.PIFs))
|
|
|
|
|
{
|
|
|
|
|
if (owner == null ||
|
|
|
|
|
pif.host == owner.opaque_ref)
|
|
|
|
|
return pif;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string VlanString(PIF pif)
|
|
|
|
|
{
|
|
|
|
|
if (pif == null || pif.VLAN == -1)
|
|
|
|
|
return Messages.SPACED_HYPHEN;
|
|
|
|
|
|
|
|
|
|
return pif.VLAN.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Return a string version of a list, in the form "L1, L2, L3 and L4"
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static string StringifyList<T>(List<T> list)
|
|
|
|
|
{
|
|
|
|
|
if (list == null)
|
|
|
|
|
return String.Empty;
|
|
|
|
|
|
|
|
|
|
StringBuilder ans = new StringBuilder();
|
|
|
|
|
for (int i = 0; i < list.Count; ++i)
|
|
|
|
|
{
|
|
|
|
|
ans.Append(list[i].ToString());
|
|
|
|
|
if (i < list.Count - 2)
|
|
|
|
|
ans.Append(Messages.STRINGIFY_LIST_INNERSEP);
|
|
|
|
|
else if (i == list.Count - 2)
|
|
|
|
|
ans.Append(Messages.STRINGIFY_LIST_LASTSEP);
|
|
|
|
|
}
|
|
|
|
|
return ans.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Does the connection support Link aggregation (LACP) bonds (i.e. on the vSwitch backend)?
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static bool SupportsLinkAggregationBond(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
Host coordinator = GetCoordinator(connection);
|
|
|
|
|
return coordinator != null && coordinator.vSwitchNetworkBackend();
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Number of alloowed NICs per bond
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static int BondSizeLimit(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
Host coordinator = GetCoordinator(connection);
|
|
|
|
|
// For hosts on the vSwitch backend, we allow 4 NICs per bond; otherwise, 2
|
|
|
|
|
return coordinator != null && coordinator.vSwitchNetworkBackend() ? 4 : 2;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
public static Host GetHostAncestor(IXenObject xenObject)
|
|
|
|
|
{
|
|
|
|
|
if (xenObject == null || xenObject.Connection == null)
|
|
|
|
|
return null;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
var h = xenObject as Host;
|
|
|
|
|
if (h != null)
|
|
|
|
|
return h;
|
2017-09-03 04:33:29 +02:00
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
var sr = xenObject as SR;
|
|
|
|
|
if (sr != null)
|
|
|
|
|
return sr.Home();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
var vm = xenObject as VM;
|
|
|
|
|
if (vm != null)
|
|
|
|
|
return vm.Home();
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
return null;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
public static bool SameServerVersion(Host host, string longProductVersion)
|
|
|
|
|
{
|
|
|
|
|
return host != null && host.LongProductVersion() == longProductVersion;
|
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
public static bool EnabledTargetExists(Host host, IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
if (host != null)
|
|
|
|
|
return host.enabled;
|
2013-06-24 13:41:48 +02:00
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
return connection.Cache.Hosts.Any(h => h.enabled);
|
|
|
|
|
}
|
2013-12-11 11:45:28 +01:00
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
public static bool GpuCapability(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
if (FeatureForbidden(connection, Host.RestrictGpu))
|
|
|
|
|
return false;
|
|
|
|
|
var pool = GetPoolOfOne(connection);
|
|
|
|
|
return pool != null && pool.HasGpu();
|
|
|
|
|
}
|
2014-05-20 13:38:03 +02:00
|
|
|
|
|
2015-01-21 11:04:47 +01:00
|
|
|
|
public static bool VGpuCapability(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
if (FeatureForbidden(connection, Host.RestrictVgpu))
|
|
|
|
|
return false;
|
|
|
|
|
var pool = GetPoolOfOne(connection);
|
2017-09-03 04:33:29 +02:00
|
|
|
|
return pool != null && pool.HasVGpu();
|
2015-01-21 11:04:47 +01:00
|
|
|
|
}
|
2014-05-20 13:38:03 +02:00
|
|
|
|
|
2015-01-21 11:04:47 +01:00
|
|
|
|
/// <summary>
|
2022-01-11 13:53:48 +01:00
|
|
|
|
/// Whether creation of VLAN 0 is allowed.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static bool VLAN0Allowed(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
Host coordinator = GetCoordinator(connection);
|
|
|
|
|
// For Creedence or later on the vSwitch backend, we allow creation of VLAN 0
|
|
|
|
|
return coordinator != null && coordinator.vSwitchNetworkBackend();
|
|
|
|
|
}
|
2015-02-23 12:01:39 +01:00
|
|
|
|
|
|
|
|
|
public static bool ContainerCapability(IXenConnection connection)
|
|
|
|
|
{
|
2023-12-30 16:42:53 +01:00
|
|
|
|
var master = GetCoordinator(connection);
|
2016-10-28 13:06:44 +02:00
|
|
|
|
if (master == null)
|
|
|
|
|
return false;
|
2018-04-10 00:46:52 +02:00
|
|
|
|
if (master.ProductBrand() == PRODUCT_BRAND_XCP_NG)
|
|
|
|
|
return true;
|
2016-10-28 13:06:44 +02:00
|
|
|
|
if (ElyOrGreater(connection))
|
2017-09-03 04:33:29 +02:00
|
|
|
|
return master.AppliedUpdates().Any(update => update.Name().ToLower().StartsWith("xscontainer"));
|
2019-01-31 00:00:56 +01:00
|
|
|
|
return master.SuppPacks().Any(suppPack => suppPack.Name.ToLower().StartsWith("xscontainer"));
|
2015-02-23 12:01:39 +01:00
|
|
|
|
}
|
2015-10-06 15:04:36 +02:00
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
public static bool PvsCacheCapability(IXenConnection connection)
|
|
|
|
|
{
|
2023-04-11 13:59:33 +02:00
|
|
|
|
if (CloudOrGreater(connection))
|
2022-03-14 12:35:20 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
2022-01-11 13:53:48 +01:00
|
|
|
|
var coordinator = GetCoordinator(connection);
|
|
|
|
|
return coordinator != null && coordinator.AppliedUpdates().Any(update => update.Name().ToLower().StartsWith("pvsaccelerator"));
|
|
|
|
|
}
|
2015-10-06 15:04:36 +02:00
|
|
|
|
|
2015-11-23 15:47:28 +01:00
|
|
|
|
public static string UrlEncode(this string str)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(str))
|
|
|
|
|
return str;
|
|
|
|
|
|
|
|
|
|
return System.Net.WebUtility.UrlEncode(str);
|
|
|
|
|
}
|
2017-08-08 13:25:18 +02:00
|
|
|
|
|
|
|
|
|
public static string UpdatesFriendlyName(string propertyName)
|
|
|
|
|
{
|
2019-01-25 01:03:17 +01:00
|
|
|
|
return FriendlyNameManager.FriendlyNames.GetString(string.Format("Label-{0}", propertyName)) ?? propertyName;
|
2017-08-08 13:25:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string UpdatesFriendlyNameAndVersion(Pool_update update)
|
|
|
|
|
{
|
|
|
|
|
var friendlyName = UpdatesFriendlyName(update.Name());
|
|
|
|
|
if (string.IsNullOrEmpty(update.version))
|
|
|
|
|
return friendlyName;
|
|
|
|
|
return string.Format(Messages.SUPP_PACK_DESCRIPTION, friendlyName, update.version);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static List<string> HostAppliedPatchesList(Host host)
|
|
|
|
|
{
|
|
|
|
|
List<string> result = new List<string>();
|
|
|
|
|
|
|
|
|
|
if (ElyOrGreater(host))
|
|
|
|
|
{
|
|
|
|
|
foreach (var update in host.AppliedUpdates())
|
|
|
|
|
result.Add(UpdatesFriendlyNameAndVersion(update));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
foreach (Pool_patch patch in host.AppliedPatches())
|
|
|
|
|
result.Add(patch.Name());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Sort(StringUtility.NaturalCompare);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2018-11-29 10:47:39 +01:00
|
|
|
|
|
|
|
|
|
public static List<string> FindIpAddresses(Dictionary<string, string> networks, string device)
|
|
|
|
|
{
|
|
|
|
|
if (networks == null || string.IsNullOrWhiteSpace(device))
|
|
|
|
|
return new List<string>();
|
|
|
|
|
|
|
|
|
|
// PR-1373 - VM_guest_metrics.networks is a dictionary of IP addresses in the format:
|
|
|
|
|
// [["0/ip", <IPv4 address>],
|
|
|
|
|
// ["0/ipv4/0", <IPv4 address>], ["0/ipv4/1", <IPv4 address>],
|
|
|
|
|
// ["0/ipv6/0", <IPv6 address>], ["0/ipv6/1", <IPv6 address>]]
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
(from network in networks
|
2022-01-11 13:53:48 +01:00
|
|
|
|
where network.Key.StartsWith(string.Format("{0}/ip", device))
|
|
|
|
|
orderby network.Key
|
|
|
|
|
select network.Value.Split(new[] { "\n", "%n" }, StringSplitOptions.RemoveEmptyEntries)).SelectMany(x => x).Distinct().ToList();
|
2018-11-29 10:47:39 +01:00
|
|
|
|
}
|
2021-08-23 11:58:41 +02:00
|
|
|
|
|
|
|
|
|
public static bool GpusAvailable(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
return connection?.Cache.GPU_groups.Any(g => g.PGPUs.Count > 0 && g.supported_VGPU_types.Count != 0) ?? false;
|
|
|
|
|
}
|
2021-10-28 14:46:00 +02:00
|
|
|
|
|
|
|
|
|
public static bool ConnectionRequiresRbac(IXenConnection connection)
|
|
|
|
|
{
|
|
|
|
|
if (connection?.Session == null)
|
|
|
|
|
throw new NullReferenceException("RBAC check was given a null connection");
|
|
|
|
|
|
|
|
|
|
if (connection.Session.IsLocalSuperuser)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return GetCoordinator(connection).external_auth_type != Auth.AUTH_TYPE_NONE;
|
|
|
|
|
}
|
2023-04-12 09:31:05 +02:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds the specified authentication token to the existing query string and returns
|
|
|
|
|
/// the modified query string.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="authToken">The authentication token to add to the query string.</param>
|
|
|
|
|
/// <param name="existingQueryString">The existing query string to add the token to.</param>
|
|
|
|
|
/// <returns>The modified query string.</returns>
|
|
|
|
|
public static string AddAuthTokenToQueryString(string authToken, string existingQueryString)
|
|
|
|
|
{
|
|
|
|
|
var queryString = existingQueryString;
|
|
|
|
|
if (string.IsNullOrEmpty(authToken))
|
|
|
|
|
{
|
|
|
|
|
return queryString;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var query = new NameValueCollection();
|
|
|
|
|
if (!string.IsNullOrEmpty(existingQueryString))
|
|
|
|
|
{
|
|
|
|
|
query.Add(HttpUtility.ParseQueryString(existingQueryString));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tokenQueryString = HttpUtility.ParseQueryString(authToken);
|
|
|
|
|
|
|
|
|
|
query.Add(tokenQueryString);
|
|
|
|
|
|
|
|
|
|
queryString = string.Join("&",
|
|
|
|
|
query.AllKeys
|
|
|
|
|
.Where(key => !string.IsNullOrWhiteSpace(key))
|
2023-04-13 12:43:44 +02:00
|
|
|
|
.Select(key => $"{key}={HttpUtility.UrlEncode(query[key])}")
|
2023-04-12 09:31:05 +02:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
log.Error(ex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return queryString;
|
|
|
|
|
}
|
2023-04-28 15:14:31 +02:00
|
|
|
|
|
2023-08-07 18:25:31 +02:00
|
|
|
|
public static bool TryLoadHostEua(Host host, string targetUri, out string eua)
|
2023-04-28 15:14:31 +02:00
|
|
|
|
{
|
|
|
|
|
eua = null;
|
|
|
|
|
if (host == null || targetUri == null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
var args = new Dictionary<string, string>
|
|
|
|
|
{
|
2023-08-07 18:25:31 +02:00
|
|
|
|
{ "url", targetUri }
|
2023-04-28 15:14:31 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var result = Host.call_plugin(host.Connection.Session, host.opaque_ref, "prepare_host_upgrade.py", "getEUA", args);
|
|
|
|
|
var jsonPayloadDefinition = new { eua = string.Empty };
|
|
|
|
|
var parsedPayload = JsonConvert.DeserializeAnonymousType(result, jsonPayloadDefinition);
|
|
|
|
|
|
|
|
|
|
eua = parsedPayload.eua;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
log.Error(ex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return eua != null;
|
2018-11-29 10:47:39 +01:00
|
|
|
|
}
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
}
|