mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2025-01-20 15:29:26 +01:00
New logic for connection to a xapi database and some connection tidy up:
- Removed event that had no subscribers. - The xml docs are more useful on the properties and not their supporting private fields. - Stop silencing cache errors as they may reveal other issues. - Adding server to history was called twice. - Connection null checks and refactoring of ConnectionExists exceptions. - Added some logging. Signed-off-by: Konstantina Chremmou <Konstantina.Chremmou@cloud.com>
This commit is contained in:
parent
8375754a5b
commit
a4df1eed61
@ -39,6 +39,7 @@ using XenAPI;
|
|||||||
using XenAdmin.Actions;
|
using XenAdmin.Actions;
|
||||||
using XenAdmin.Actions.Wlb;
|
using XenAdmin.Actions.Wlb;
|
||||||
using XenAdmin.Commands;
|
using XenAdmin.Commands;
|
||||||
|
using XenAdmin.Network;
|
||||||
|
|
||||||
|
|
||||||
namespace XenAdmin.Controls.Wlb
|
namespace XenAdmin.Controls.Wlb
|
||||||
@ -526,7 +527,7 @@ namespace XenAdmin.Controls.Wlb
|
|||||||
{
|
{
|
||||||
Program.AssertOnEventThread();
|
Program.AssertOnEventThread();
|
||||||
|
|
||||||
if(_xenObject == null)
|
if (_xenObject == null || _xenObject.Connection is XenConnection conn && conn.IsSimulatedConnection)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Helpers.WlbEnabled(_xenObject.Connection))
|
if (Helpers.WlbEnabled(_xenObject.Connection))
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using XenAdmin.Core;
|
using XenAdmin.Core;
|
||||||
using XenAdmin.Network;
|
using XenAdmin.Network;
|
||||||
@ -146,19 +147,12 @@ namespace XenAdmin.Dialogs
|
|||||||
conn = connection;
|
conn = connection;
|
||||||
|
|
||||||
foreach (var server in servers)
|
foreach (var server in servers)
|
||||||
{
|
ConnectToServer(conn, server, username, password);
|
||||||
StringUtility.ParseHostnamePort(server, out var hostname, out var port);
|
|
||||||
|
|
||||||
if (port == 0)
|
|
||||||
port = ConnectionsManager.DEFAULT_XEN_PORT;
|
|
||||||
|
|
||||||
ConnectToServer(conn, hostname, port, username, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConnectToServer(IXenConnection conn, string hostname, int port, string username, string password)
|
private void ConnectToServer(IXenConnection conn, string server, string username, string password)
|
||||||
{
|
{
|
||||||
if (conn == null)
|
if (conn == null)
|
||||||
{
|
{
|
||||||
@ -170,14 +164,27 @@ namespace XenAdmin.Dialogs
|
|||||||
conn.EndConnect(); // in case we're already connected
|
conn.EndConnect(); // in case we're already connected
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.Hostname = hostname;
|
|
||||||
conn.Port = port;
|
|
||||||
conn.Username = username;
|
conn.Username = username;
|
||||||
conn.Password = password;
|
conn.Password = password;
|
||||||
conn.ExpectPasswordIsCorrect = false;
|
conn.ExpectPasswordIsCorrect = false;
|
||||||
|
|
||||||
if (!_changedPass)
|
if (File.Exists(server))
|
||||||
|
{
|
||||||
|
conn.Hostname = server;
|
||||||
|
conn.Port = ConnectionsManager.DEFAULT_XEN_PORT;
|
||||||
|
XenConnectionUI.ConnectToXapiDatabase(conn, this);
|
||||||
|
}
|
||||||
|
else if (!_changedPass)
|
||||||
|
{
|
||||||
|
StringUtility.ParseHostnamePort(server, out var hostname, out var port);
|
||||||
|
|
||||||
|
if (port == 0)
|
||||||
|
port = ConnectionsManager.DEFAULT_XEN_PORT;
|
||||||
|
|
||||||
|
conn.Hostname = hostname;
|
||||||
|
conn.Port = port;
|
||||||
XenConnectionUI.BeginConnect(conn, true, Owner, false);
|
XenConnectionUI.BeginConnect(conn, true, Owner, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void conn_CachePopulated(IXenConnection conn)
|
private void conn_CachePopulated(IXenConnection conn)
|
||||||
|
@ -62,7 +62,7 @@ namespace XenAdmin.Dialogs
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
UserDetails ud = session.CurrentUserDetails;
|
UserDetails ud = session.CurrentUserDetails;
|
||||||
labelCurrentUserValue.Text = ud.UserDisplayName ?? ud.UserName ?? Messages.UNKNOWN_AD_USER;
|
labelCurrentUserValue.Text = ud?.UserDisplayName ?? ud?.UserName ?? Messages.UNKNOWN_AD_USER;
|
||||||
labelCurrentRoleValue.Text = Role.FriendlyCSVRoleList(session.Roles);
|
labelCurrentRoleValue.Text = Role.FriendlyCSVRoleList(session.Roles);
|
||||||
authorizedRoles.Sort((r1, r2) => r2.CompareTo(r1));
|
authorizedRoles.Sort((r1, r2) => r2.CompareTo(r1));
|
||||||
labelRequiredRoleValue.Text = Role.FriendlyCSVRoleList(authorizedRoles);
|
labelRequiredRoleValue.Text = Role.FriendlyCSVRoleList(authorizedRoles);
|
||||||
|
@ -44,6 +44,18 @@ namespace XenAdmin.Network
|
|||||||
{
|
{
|
||||||
public static Dictionary<IXenConnection, ConnectingToServerDialog> connectionDialogs = new Dictionary<IXenConnection, ConnectingToServerDialog>();
|
public static Dictionary<IXenConnection, ConnectingToServerDialog> connectionDialogs = new Dictionary<IXenConnection, ConnectingToServerDialog>();
|
||||||
|
|
||||||
|
public static void ConnectToXapiDatabase(IXenConnection conn, Form owner)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
((XenConnection)conn).LoadFromDatabaseFile(conn.Hostname);
|
||||||
|
}
|
||||||
|
catch (ConnectionExists e)
|
||||||
|
{
|
||||||
|
ShowConnectingDialogError(owner, conn, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start connecting to a server
|
/// Start connecting to a server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -254,7 +266,7 @@ namespace XenAdmin.Network
|
|||||||
|
|
||||||
if (!Program.RunInAutomatedTestMode)
|
if (!Program.RunInAutomatedTestMode)
|
||||||
{
|
{
|
||||||
using (var dlg = new InformationDialog(c.GetDialogMessage(connection)))
|
using (var dlg = new InformationDialog(c.GetDialogMessage()))
|
||||||
dlg.ShowDialog(owner);
|
dlg.ShowDialog(owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
181
XenModel/Db.cs
Normal file
181
XenModel/Db.cs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
/* Copyright (c) Cloud Software Group, Inc.
|
||||||
|
*
|
||||||
|
* 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.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Xml;
|
||||||
|
using XenAdmin.Core;
|
||||||
|
using XenAPI;
|
||||||
|
|
||||||
|
namespace XenAdmin
|
||||||
|
{
|
||||||
|
public class Db
|
||||||
|
{
|
||||||
|
private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
public Db(XmlDocument document)
|
||||||
|
{
|
||||||
|
XmlNode dbNode = document.ChildNodes.Cast<XmlNode>().FirstOrDefault(node => node.Name == "database") ??
|
||||||
|
throw new ArgumentException("Couldn't find database tag in state db");
|
||||||
|
|
||||||
|
foreach (XmlNode n in dbNode.ChildNodes)
|
||||||
|
{
|
||||||
|
if (n.Name != "table")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Tables.Add(new DbTable(n));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_log.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DbTable> Tables { get; } = new List<DbTable>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Corresponds to an API class type. Its Rows are the class instances (API objects)
|
||||||
|
/// </summary>
|
||||||
|
public class DbTable
|
||||||
|
{
|
||||||
|
private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
private static readonly Type[] AssemblyTypes = Assembly.GetExecutingAssembly().GetTypes();
|
||||||
|
|
||||||
|
public DbTable(XmlNode tableNode)
|
||||||
|
{
|
||||||
|
var name = tableNode.Attributes?["name"].Value;
|
||||||
|
|
||||||
|
ApiClassType = AssemblyTypes
|
||||||
|
.Where(t => t.Namespace == nameof(XenAPI))
|
||||||
|
.FirstOrDefault(t => string.Equals(t.Name, name, StringComparison.CurrentCultureIgnoreCase));
|
||||||
|
|
||||||
|
foreach (XmlNode n in tableNode.ChildNodes)
|
||||||
|
{
|
||||||
|
if (n.Name != "row")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Rows.Add(new Row(ApiClassType, n));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_log.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type ApiClassType { get; }
|
||||||
|
|
||||||
|
public List<Row> Rows { get; } = new List<Row>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Corresponds to an API object
|
||||||
|
/// </summary>
|
||||||
|
public class Row
|
||||||
|
{
|
||||||
|
private readonly XmlNode _rowNode;
|
||||||
|
|
||||||
|
public Row(Type apiClassType, XmlNode rowNode)
|
||||||
|
{
|
||||||
|
_rowNode = rowNode;
|
||||||
|
|
||||||
|
HashTable = ConvertToHashtable(out var opaqueRef);
|
||||||
|
XenObject = ConvertToXenObject(apiClassType);
|
||||||
|
|
||||||
|
if (XenObject != null)
|
||||||
|
XenObject.opaque_ref = opaqueRef;
|
||||||
|
|
||||||
|
ObjectChange = new ObjectChange(apiClassType, opaqueRef, XenObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Hashtable HashTable { get; }
|
||||||
|
|
||||||
|
public IXenObject XenObject { get; }
|
||||||
|
|
||||||
|
public ObjectChange ObjectChange { get; }
|
||||||
|
|
||||||
|
private Hashtable ConvertToHashtable(out string opaqueRef)
|
||||||
|
{
|
||||||
|
opaqueRef = "OpaqueRef:NULL";
|
||||||
|
|
||||||
|
if (_rowNode.Attributes == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var hashtable = new Hashtable();
|
||||||
|
|
||||||
|
foreach (XmlAttribute attr in _rowNode.Attributes)
|
||||||
|
{
|
||||||
|
if (attr.Name.StartsWith("__"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (attr.Name == "_ref")
|
||||||
|
{
|
||||||
|
opaqueRef = attr.Value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = attr.Name.Replace("__", "_");
|
||||||
|
|
||||||
|
hashtable[key] = EscapeXapiChars(attr.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashtable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IXenObject ConvertToXenObject(Type apiClassType)
|
||||||
|
{
|
||||||
|
return apiClassType?.GetConstructor(new[] { typeof(Hashtable) })?.Invoke(new object[] { HashTable }) as IXenObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string EscapeXapiChars(string input)
|
||||||
|
{
|
||||||
|
input = input.Replace("%.", " ");
|
||||||
|
input = input.Replace("%_", " ");
|
||||||
|
input = input.Replace("%n", "\n");
|
||||||
|
input = input.Replace("%r", "\r");
|
||||||
|
input = input.Replace("%t", "\t");
|
||||||
|
input = input.Replace("%%", "%");
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -62,9 +62,7 @@ namespace XenAdmin.Network
|
|||||||
Session DuplicateSession(int timeout);
|
Session DuplicateSession(int timeout);
|
||||||
void EndConnect(bool resetState = true, bool exiting = false);
|
void EndConnect(bool resetState = true, bool exiting = false);
|
||||||
void Interrupt();
|
void Interrupt();
|
||||||
Session Connect(string user, string password);
|
|
||||||
List<string> PoolMembers { get; set; }
|
List<string> PoolMembers { get; set; }
|
||||||
void LoadCache(Session session);
|
|
||||||
bool SuppressErrors { get; set; }
|
bool SuppressErrors { get; set; }
|
||||||
bool PreventResettingPasswordPrompt { get;set; }
|
bool PreventResettingPasswordPrompt { get;set; }
|
||||||
bool CoordinatorMayChange { get; set; }
|
bool CoordinatorMayChange { get; set; }
|
||||||
@ -83,7 +81,6 @@ namespace XenAdmin.Network
|
|||||||
void WaitFor(Func<bool> predicate, Func<bool> cancelling);
|
void WaitFor(Func<bool> predicate, Func<bool> cancelling);
|
||||||
TimeSpan ServerTimeOffset { get; set; }
|
TimeSpan ServerTimeOffset { get; set; }
|
||||||
Session Session { get; }
|
Session Session { get; }
|
||||||
event EventHandler<EventArgs> TimeSkewUpdated;
|
|
||||||
string UriScheme { get; }
|
string UriScheme { get; }
|
||||||
event EventHandler<EventArgs> XenObjectsUpdated;
|
event EventHandler<EventArgs> XenObjectsUpdated;
|
||||||
NetworkCredential NetworkCredential { get; set; }
|
NetworkCredential NetworkCredential { get; set; }
|
||||||
|
@ -39,7 +39,9 @@ using XenAdmin.Core;
|
|||||||
using XenAPI;
|
using XenAPI;
|
||||||
using XenCenterLib;
|
using XenCenterLib;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Xml;
|
||||||
using System.Xml.Serialization;
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
|
||||||
@ -71,13 +73,20 @@ namespace XenAdmin.Network
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool fromDialog = false;
|
public bool fromDialog = false;
|
||||||
|
|
||||||
|
private volatile bool _expectedDisruption;
|
||||||
|
private volatile bool _suppressErrors;
|
||||||
|
private volatile bool _preventResettingPasswordPrompt;
|
||||||
|
private volatile bool _coordinatorMayChange;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether we're expecting network disruption, say because we're reconfiguring the network at this time. In that case, we ignore
|
/// Whether we're expecting network disruption, say because we're reconfiguring the network at this time. In that case, we ignore
|
||||||
/// keepalive failures, and expect task polling to be disrupted.
|
/// keepalive failures, and expect task polling to be disrupted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private volatile bool _expectedDisruption = false;
|
public bool ExpectDisruption
|
||||||
|
{
|
||||||
public bool ExpectDisruption { get { return _expectedDisruption; } set { _expectedDisruption = value; } }
|
get => _expectedDisruption;
|
||||||
|
set => _expectedDisruption = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If we are 'expecting' this connection's Password property to contain the correct password
|
/// If we are 'expecting' this connection's Password property to contain the correct password
|
||||||
@ -88,16 +97,12 @@ namespace XenAdmin.Network
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used by the patch wizard, suppress any errors coming from reconnect attempts
|
/// Used by the patch wizard, suppress any errors coming from reconnect attempts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private volatile bool _suppressErrors;
|
|
||||||
public bool SuppressErrors
|
public bool SuppressErrors
|
||||||
{
|
{
|
||||||
get => _suppressErrors;
|
get => _suppressErrors;
|
||||||
set => _suppressErrors = value;
|
set => _suppressErrors = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private volatile bool _preventResettingPasswordPrompt;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The password prompting function (<see cref="_promptForNewPassword"/>) is set to null when the connection is closed.
|
/// The password prompting function (<see cref="_promptForNewPassword"/>) is set to null when the connection is closed.
|
||||||
/// Set this value to true in order to prevent that from happening.
|
/// Set this value to true in order to prevent that from happening.
|
||||||
@ -112,9 +117,11 @@ namespace XenAdmin.Network
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates whether we are expecting the pool coordinator to change soon (e.g. when explicitly designating a new coordinator).
|
/// Indicates whether we are expecting the pool coordinator to change soon (e.g. when explicitly designating a new coordinator).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private volatile bool _coordinatorMayChange = false;
|
public bool CoordinatorMayChange
|
||||||
|
{
|
||||||
public bool CoordinatorMayChange { get { return _coordinatorMayChange; } set { _coordinatorMayChange = value; } }
|
get => _coordinatorMayChange;
|
||||||
|
set => _coordinatorMayChange = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set when we detect that Event.next() has become blocked and we need to reset the connection. See CA-33145.
|
/// Set when we detect that Event.next() has become blocked and we need to reset the connection. See CA-33145.
|
||||||
@ -132,11 +139,15 @@ namespace XenAdmin.Network
|
|||||||
private string CoordinatorIPAddress = "";
|
private string CoordinatorIPAddress = "";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The lock that must be taken around connectTask and monitor.
|
/// The lock that must be taken around connectTask and heartbeat.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly object connectTaskLock = new object();
|
private readonly object connectTaskLock = new object();
|
||||||
private ConnectTask connectTask = null;
|
private ConnectTask connectTask = null;
|
||||||
private Heartbeat heartbeat = null;
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is the metrics monitor. Has to be accessed within the connectTaskLock.
|
||||||
|
/// </summary>
|
||||||
|
private Heartbeat heartbeat;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether we are trying to automatically connect to the new coordinator. Set in HandleConnectionLost.
|
/// Whether we are trying to automatically connect to the new coordinator. Set in HandleConnectionLost.
|
||||||
@ -166,7 +177,7 @@ namespace XenAdmin.Network
|
|||||||
|
|
||||||
private DateTime m_startTime = DateTime.MinValue;
|
private DateTime m_startTime = DateTime.MinValue;
|
||||||
private int m_lastDebug;
|
private int m_lastDebug;
|
||||||
private TimeSpan ServerTimeOffset_ = TimeSpan.Zero;
|
private TimeSpan _serverTimeOffset = TimeSpan.Zero;
|
||||||
private object ServerTimeOffsetLock = new object();
|
private object ServerTimeOffsetLock = new object();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The offset between the clock at the client and the clock at the server. server time + ServerTimeOffset = client time.
|
/// The offset between the clock at the client and the clock at the server. server time + ServerTimeOffset = client time.
|
||||||
@ -179,15 +190,14 @@ namespace XenAdmin.Network
|
|||||||
{
|
{
|
||||||
lock (ServerTimeOffsetLock)
|
lock (ServerTimeOffsetLock)
|
||||||
{
|
{
|
||||||
return ServerTimeOffset_;
|
return _serverTimeOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
lock (ServerTimeOffsetLock)
|
lock (ServerTimeOffsetLock)
|
||||||
{
|
{
|
||||||
TimeSpan diff = ServerTimeOffset_ - value;
|
TimeSpan diff = _serverTimeOffset - value;
|
||||||
|
|
||||||
if (diff.TotalSeconds < -1 || 1 < diff.TotalSeconds)
|
if (diff.TotalSeconds < -1 || 1 < diff.TotalSeconds)
|
||||||
{
|
{
|
||||||
@ -212,11 +222,8 @@ namespace XenAdmin.Network
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerTimeOffset_ = value;
|
_serverTimeOffset = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TimeSkewUpdated != null)
|
|
||||||
TimeSkewUpdated(this, EventArgs.Empty);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,8 +257,7 @@ namespace XenAdmin.Network
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The cache of XenAPI objects for this connection.
|
/// The cache of XenAPI objects for this connection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ICache _cache = new Cache();
|
public ICache Cache { get; } = new Cache();
|
||||||
public ICache Cache { get { return _cache; } }
|
|
||||||
|
|
||||||
private readonly LockFreeQueue<ObjectChange> eventQueue = new LockFreeQueue<ObjectChange>();
|
private readonly LockFreeQueue<ObjectChange> eventQueue = new LockFreeQueue<ObjectChange>();
|
||||||
private readonly System.Threading.Timer cacheUpdateTimer;
|
private readonly System.Threading.Timer cacheUpdateTimer;
|
||||||
@ -259,12 +265,20 @@ namespace XenAdmin.Network
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the cache for this connection has been populated.
|
/// Whether the cache for this connection has been populated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool cacheIsPopulated = false;
|
|
||||||
public bool CacheIsPopulated
|
public bool CacheIsPopulated
|
||||||
{
|
{
|
||||||
get { return cacheIsPopulated; }
|
get => _cacheIsPopulated;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_cacheIsPopulated = value;
|
||||||
|
|
||||||
|
if (_cacheIsPopulated)
|
||||||
|
CachePopulated?.Invoke(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _cacheIsPopulated;
|
||||||
|
|
||||||
private bool cacheUpdaterRunning = false;
|
private bool cacheUpdaterRunning = false;
|
||||||
private bool updatesWaiting = false;
|
private bool updatesWaiting = false;
|
||||||
|
|
||||||
@ -280,56 +294,7 @@ namespace XenAdmin.Network
|
|||||||
cacheUpdateTimer = new Timer(cacheUpdater);
|
cacheUpdateTimer = new Timer(cacheUpdater);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
#region Events
|
||||||
/// For use by unit tests only.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user"></param>
|
|
||||||
/// <param name="password"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public Session Connect(string user, string password)
|
|
||||||
{
|
|
||||||
heartbeat = new Heartbeat(this, XenAdminConfigManager.Provider.ConnectionTimeout);
|
|
||||||
Session session = new Session(Session.STANDARD_TIMEOUT, this, Hostname, Port);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
session.login_with_password(user, password, Helper.APIVersionString(API_Version.LATEST), Session.UserAgent);
|
|
||||||
|
|
||||||
// this is required so connection.IsConnected returns true in the unit tests.
|
|
||||||
connectTask = new ConnectTask("test", 0);
|
|
||||||
connectTask.Connected = true;
|
|
||||||
connectTask.Session = session;
|
|
||||||
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used by unit tests only.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="session">The session.</param>
|
|
||||||
public void LoadCache(Session session)
|
|
||||||
{
|
|
||||||
this.Cache.Clear();
|
|
||||||
|
|
||||||
cacheIsPopulated = false;
|
|
||||||
|
|
||||||
string token = "";
|
|
||||||
XenObjectDownloader.GetAllObjects(session, eventQueue, () => false, ref token);
|
|
||||||
List<ObjectChange> events = new List<ObjectChange>();
|
|
||||||
|
|
||||||
while (eventQueue.NotEmpty)
|
|
||||||
events.Add(eventQueue.Dequeue());
|
|
||||||
|
|
||||||
this.Cache.UpdateFrom(this, events);
|
|
||||||
cacheIsPopulated = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired just before the cache is cleared (i.e. the cache is still populated).
|
/// Fired just before the cache is cleared (i.e. the cache is still populated).
|
||||||
@ -351,8 +316,48 @@ namespace XenAdmin.Network
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<EventArgs> XenObjectsUpdated;
|
public event EventHandler<EventArgs> XenObjectsUpdated;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public void LoadFromDatabaseFile(string databaseFile)
|
||||||
|
{
|
||||||
|
IsSimulatedConnection = true;
|
||||||
|
|
||||||
|
var foundConn = ConnectionsManager.XenConnections.Cast<XenConnection>().FirstOrDefault(c =>
|
||||||
|
c.Hostname == databaseFile && c.CoordinatorIPAddress == CoordinatorIPAddress);
|
||||||
|
if (foundConn != null && foundConn.IsConnected)
|
||||||
|
throw new ConnectionExists(this);
|
||||||
|
|
||||||
|
var document = new XmlDocument();
|
||||||
|
using (var sr = new StreamReader(databaseFile))
|
||||||
|
document.LoadXml(sr.ReadToEnd());
|
||||||
|
|
||||||
|
var db = new Db(document);
|
||||||
|
var events = db.Tables.SelectMany(t => t.Rows).Select(r => r.ObjectChange).ToList();
|
||||||
|
|
||||||
|
var session = new Session(Session.STANDARD_TIMEOUT, this, Path.GetFileName(Hostname), Port);
|
||||||
|
connectTask = new ConnectTask(Hostname, Port) { Connected = true, Session = session };
|
||||||
|
|
||||||
|
OnBeforeMajorChange(false);
|
||||||
|
Cache.Clear();
|
||||||
|
CacheIsPopulated = false;
|
||||||
|
OnAfterMajorChange(false);
|
||||||
|
|
||||||
|
OnBeforeMajorChange(false);
|
||||||
|
Cache.UpdateFrom(this, events);
|
||||||
|
OnAfterMajorChange(false);
|
||||||
|
CacheIsPopulated = true;
|
||||||
|
|
||||||
|
var pool = Cache.Pools[0];
|
||||||
|
CoordinatorIPAddress = Cache.Hosts.FirstOrDefault(h => h.opaque_ref == pool.master)?.address;
|
||||||
|
|
||||||
|
HandleSuccessfulConnection(connectTask, pool);
|
||||||
|
MarkConnectActionComplete();
|
||||||
|
OnXenObjectsUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
public NetworkCredential NetworkCredential { get; set; }
|
public NetworkCredential NetworkCredential { get; set; }
|
||||||
public event EventHandler<EventArgs> TimeSkewUpdated;
|
|
||||||
|
public bool IsSimulatedConnection { get; private set; }
|
||||||
|
|
||||||
public bool IsConnected
|
public bool IsConnected
|
||||||
{
|
{
|
||||||
@ -608,6 +613,8 @@ namespace XenAdmin.Network
|
|||||||
|
|
||||||
lock (connectTaskLock)
|
lock (connectTaskLock)
|
||||||
{
|
{
|
||||||
|
//if connectTask != null a connection is already in progress
|
||||||
|
|
||||||
if (connectTask == null)
|
if (connectTask == null)
|
||||||
{
|
{
|
||||||
ClearEventQueue();
|
ClearEventQueue();
|
||||||
@ -615,17 +622,16 @@ namespace XenAdmin.Network
|
|||||||
Cache.Clear();
|
Cache.Clear();
|
||||||
OnAfterMajorChange(false);
|
OnAfterMajorChange(false);
|
||||||
connectTask = new ConnectTask(Hostname, Port);
|
connectTask = new ConnectTask(Hostname, Port);
|
||||||
StopMonitor();
|
|
||||||
|
heartbeat?.Stop();
|
||||||
|
heartbeat = null;
|
||||||
heartbeat = new Heartbeat(this, XenAdminConfigManager.Provider.ConnectionTimeout);
|
heartbeat = new Heartbeat(this, XenAdminConfigManager.Provider.ConnectionTimeout);
|
||||||
|
|
||||||
Thread t = new Thread(ConnectWorkerThread);
|
Thread t = new Thread(ConnectWorkerThread);
|
||||||
t.Name = "Connection to " + Hostname;
|
t.Name = "Connection to " + Hostname;
|
||||||
t.IsBackground = true;
|
t.IsBackground = true;
|
||||||
t.Start(connectTask);
|
t.Start(connectTask);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// a connection is already in progress
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -745,7 +751,9 @@ namespace XenAdmin.Network
|
|||||||
|
|
||||||
lock (connectTaskLock)
|
lock (connectTaskLock)
|
||||||
{
|
{
|
||||||
StopMonitor();
|
heartbeat?.Stop();
|
||||||
|
heartbeat = null;
|
||||||
|
|
||||||
if (task != null)
|
if (task != null)
|
||||||
{
|
{
|
||||||
task.Cancelled = true;
|
task.Cancelled = true;
|
||||||
@ -759,6 +767,7 @@ namespace XenAdmin.Network
|
|||||||
}
|
}
|
||||||
|
|
||||||
MarkConnectActionComplete();
|
MarkConnectActionComplete();
|
||||||
|
log.Info($"Connection to {Hostname} is ended.");
|
||||||
|
|
||||||
// Save list of addresses of current hosts in pool
|
// Save list of addresses of current hosts in pool
|
||||||
List<string> members = new List<string>();
|
List<string> members = new List<string>();
|
||||||
@ -861,18 +870,6 @@ namespace XenAdmin.Network
|
|||||||
Cache.Clear();
|
Cache.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCachePopulated()
|
|
||||||
{
|
|
||||||
lock (connectTaskLock)
|
|
||||||
{
|
|
||||||
if (heartbeat != null)
|
|
||||||
heartbeat.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
CachePopulated?.Invoke(this);
|
|
||||||
MarkConnectActionComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetReason(Exception error)
|
private string GetReason(Exception error)
|
||||||
{
|
{
|
||||||
if (error is ArgumentException)
|
if (error is ArgumentException)
|
||||||
@ -930,8 +927,16 @@ namespace XenAdmin.Network
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleSuccessfulConnection(string taskHostname, int task_port)
|
private void HandleSuccessfulConnection(ConnectTask task, Pool pool)
|
||||||
{
|
{
|
||||||
|
// Remove any other (disconnected) entries for this server from the tree
|
||||||
|
|
||||||
|
var existingConnections = ConnectionsManager.XenConnections.Where(c =>
|
||||||
|
c.Hostname.Equals(task.Hostname) && !c.IsConnected).ToList();
|
||||||
|
|
||||||
|
foreach (var conn in existingConnections)
|
||||||
|
ConnectionsManager.ClearCacheAndRemoveConnection(conn);
|
||||||
|
|
||||||
// add server name to history (if it's not already there)
|
// add server name to history (if it's not already there)
|
||||||
XenAdminConfigManager.Provider.UpdateServerHistory(HostnameWithPort);
|
XenAdminConfigManager.Provider.UpdateServerHistory(HostnameWithPort);
|
||||||
|
|
||||||
@ -945,18 +950,20 @@ namespace XenAdmin.Network
|
|||||||
InvokeHelper.Invoke(XenAdminConfigManager.Provider.SaveSettingsIfRequired);
|
InvokeHelper.Invoke(XenAdminConfigManager.Provider.SaveSettingsIfRequired);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.InfoFormat("Connected to {0} ({1}:{2})", FriendlyName, taskHostname, task_port);
|
string name = string.IsNullOrEmpty(FriendlyName) || FriendlyName == task.Hostname
|
||||||
string name = string.IsNullOrEmpty(FriendlyName) || FriendlyName == taskHostname
|
? task.Hostname
|
||||||
? taskHostname
|
: string.Format("{0} ({1})", FriendlyName, task.Hostname);
|
||||||
: string.Format("{0} ({1})", FriendlyName, taskHostname);
|
|
||||||
string title = string.Format(Messages.CONNECTING_NOTICE_TITLE, name);
|
string title = string.Format(Messages.CONNECTING_NOTICE_TITLE, name);
|
||||||
string msg = string.Format(Messages.CONNECTING_NOTICE_TEXT, name);
|
string msg = string.Format(Messages.CONNECTING_NOTICE_TEXT, name);
|
||||||
log.Info($"Connecting to {name} in progress.");
|
|
||||||
|
|
||||||
ConnectAction = new DummyAction(title, msg);
|
ConnectAction = new DummyAction(title, msg);
|
||||||
|
SetPoolAndHostInAction(ConnectAction, pool, PoolOpaqueRef);
|
||||||
|
|
||||||
ExpectPasswordIsCorrect = true;
|
ExpectPasswordIsCorrect = true;
|
||||||
OnConnectionResult(true, null, null);
|
OnConnectionResult(true, null, null);
|
||||||
|
|
||||||
|
log.InfoFormat("Completed connection phase for pool {0} ({1}:{2}, {3}).",
|
||||||
|
FriendlyName, task.Hostname, task.Port, PoolOpaqueRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -974,13 +981,6 @@ namespace XenAdmin.Network
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleConnectionTermination()
|
|
||||||
{
|
|
||||||
// clean up action so we dont stay open forever
|
|
||||||
if (ConnectAction != null)
|
|
||||||
ConnectAction.IsCompleted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly object PromptLock = new object();
|
private readonly object PromptLock = new object();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prompts the user for the changed password for a server.
|
/// Prompts the user for the changed password for a server.
|
||||||
@ -1066,6 +1066,7 @@ namespace XenAdmin.Network
|
|||||||
// on the GUI thread to be included in this set of
|
// on the GUI thread to be included in this set of
|
||||||
// updates, otherwise we might hose the gui thread
|
// updates, otherwise we might hose the gui thread
|
||||||
// during an event storm (ie deleting 1000 vms)
|
// during an event storm (ie deleting 1000 vms)
|
||||||
|
|
||||||
List<ObjectChange> events = new List<ObjectChange>();
|
List<ObjectChange> events = new List<ObjectChange>();
|
||||||
|
|
||||||
while (eventQueue.NotEmpty)
|
while (eventQueue.NotEmpty)
|
||||||
@ -1073,43 +1074,24 @@ namespace XenAdmin.Network
|
|||||||
|
|
||||||
if (events.Count > 0)
|
if (events.Count > 0)
|
||||||
{
|
{
|
||||||
InvokeHelper.Invoke(delegate ()
|
InvokeHelper.Invoke(() =>
|
||||||
{
|
{
|
||||||
try
|
OnBeforeMajorChange(false);
|
||||||
{
|
bool fire = Cache.UpdateFrom(this, events);
|
||||||
OnBeforeMajorChange(false);
|
OnAfterMajorChange(false);
|
||||||
bool fire = Cache.UpdateFrom(this, events);
|
|
||||||
OnAfterMajorChange(false);
|
|
||||||
|
|
||||||
if (fire)
|
|
||||||
OnXenObjectsUpdated();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
log.Error("Exception updating cache.", e);
|
|
||||||
#if DEBUG
|
|
||||||
if (System.Diagnostics.Debugger.IsAttached)
|
|
||||||
throw;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (fire)
|
||||||
|
OnXenObjectsUpdated();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!cacheIsPopulated)
|
if (!CacheIsPopulated)
|
||||||
{
|
{
|
||||||
cacheIsPopulated = true;
|
lock (connectTaskLock)
|
||||||
try
|
heartbeat?.Start();
|
||||||
{
|
|
||||||
OnCachePopulated();
|
CacheIsPopulated = true;
|
||||||
}
|
MarkConnectActionComplete();
|
||||||
catch (Exception e)
|
log.Info($"Connection to {Hostname} successful.");
|
||||||
{
|
|
||||||
log.Error("Exception calling OnCachePopulated.", e);
|
|
||||||
#if DEBUG
|
|
||||||
if (System.Diagnostics.Debugger.IsAttached)
|
|
||||||
throw;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1123,7 +1105,6 @@ namespace XenAdmin.Network
|
|||||||
{
|
{
|
||||||
string title = string.Format(Messages.CONNECTION_OK_NOTICE_TITLE, Hostname);
|
string title = string.Format(Messages.CONNECTION_OK_NOTICE_TITLE, Hostname);
|
||||||
string msg = string.Format(Messages.CONNECTION_OK_NOTICE_TEXT, Hostname);
|
string msg = string.Format(Messages.CONNECTION_OK_NOTICE_TEXT, Hostname);
|
||||||
log.Info($"Connection to {Hostname} successful.");
|
|
||||||
ConnectAction.Title = title;
|
ConnectAction.Title = title;
|
||||||
ConnectAction.Description = msg;
|
ConnectAction.Description = msg;
|
||||||
|
|
||||||
@ -1138,26 +1119,8 @@ namespace XenAdmin.Network
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stops this connection's XenMetricsMonitor thread.
|
|
||||||
/// Expects to be locked under connectTaskLock.
|
|
||||||
/// </summary>
|
|
||||||
private void StopMonitor()
|
|
||||||
{
|
|
||||||
if (heartbeat != null)
|
|
||||||
{
|
|
||||||
heartbeat.Stop();
|
|
||||||
heartbeat = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const int DEFAULT_MAX_SESSION_LOGIN_ATTEMPTS = 3;
|
private const int DEFAULT_MAX_SESSION_LOGIN_ATTEMPTS = 3;
|
||||||
|
|
||||||
public static bool IsSimulatorConnection(string url)
|
|
||||||
{
|
|
||||||
return url.EndsWith(".db") || url.EndsWith(".xml") || url.EndsWith(".tmp");
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly string eventNextConnectionGroupName = Guid.NewGuid().ToString();
|
private readonly string eventNextConnectionGroupName = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -1186,7 +1149,7 @@ namespace XenAdmin.Network
|
|||||||
Session eventNextSession = DuplicateSession(EVENT_NEXT_TIMEOUT);
|
Session eventNextSession = DuplicateSession(EVENT_NEXT_TIMEOUT);
|
||||||
eventNextSession.ConnectionGroupName = eventNextConnectionGroupName; // this will force the eventNextSession onto its own set of TCP streams (see CA-108676)
|
eventNextSession.ConnectionGroupName = eventNextConnectionGroupName; // this will force the eventNextSession onto its own set of TCP streams (see CA-108676)
|
||||||
|
|
||||||
cacheIsPopulated = false;
|
CacheIsPopulated = false;
|
||||||
session.CacheWarming = true;
|
session.CacheWarming = true;
|
||||||
|
|
||||||
string token = "";
|
string token = "";
|
||||||
@ -1207,14 +1170,18 @@ namespace XenAdmin.Network
|
|||||||
OnConnectionMessageChanged(string.Format(Messages.LABEL_SYNC, this.Hostname));
|
OnConnectionMessageChanged(string.Format(Messages.LABEL_SYNC, this.Hostname));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debug("Cache is warming. Starting XenObjectDownloader.GetAllObjects");
|
||||||
XenObjectDownloader.GetAllObjects(session, eventQueue, task.GetCancelled, ref token);
|
XenObjectDownloader.GetAllObjects(session, eventQueue, task.GetCancelled, ref token);
|
||||||
|
log.Debug("Cache is warming. XenObjectDownloader.GetAllObjects finished successfully");
|
||||||
session.CacheWarming = false;
|
session.CacheWarming = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
log.Debug("Starting XenObjectDownloader.GetEvents");
|
||||||
XenObjectDownloader.GetEvents(eventNextSession, eventQueue, task.GetCancelled, ref token);
|
XenObjectDownloader.GetEvents(eventNextSession, eventQueue, task.GetCancelled, ref token);
|
||||||
|
log.Debug("Starting XenObjectDownloader.GetEvents finished successfully");
|
||||||
eventsExceptionLogged = false;
|
eventsExceptionLogged = false;
|
||||||
}
|
}
|
||||||
catch (Exception exn)
|
catch (Exception exn)
|
||||||
@ -1225,8 +1192,8 @@ namespace XenAdmin.Network
|
|||||||
log.DebugFormat("Exception (disruption is expected) in XenObjectDownloader.GetEvents: {0}", exn.GetType().Name);
|
log.DebugFormat("Exception (disruption is expected) in XenObjectDownloader.GetEvents: {0}", exn.GetType().Name);
|
||||||
|
|
||||||
// ignoring some exceptions when disruption is expected
|
// ignoring some exceptions when disruption is expected
|
||||||
if (exn is System.IO.IOException ||
|
if (exn is IOException ||
|
||||||
(exn is WebException && ((exn as WebException).Status == WebExceptionStatus.KeepAliveFailure || (exn as WebException).Status == WebExceptionStatus.ConnectFailure)))
|
(exn is WebException webEx && (webEx.Status == WebExceptionStatus.KeepAliveFailure || webEx.Status == WebExceptionStatus.ConnectFailure)))
|
||||||
{
|
{
|
||||||
if (!eventsExceptionLogged)
|
if (!eventsExceptionLogged)
|
||||||
{
|
{
|
||||||
@ -1269,16 +1236,10 @@ namespace XenAdmin.Network
|
|||||||
bool sameCoordinator = CoordinatorIPAddress == connection.CoordinatorIPAddress;
|
bool sameCoordinator = CoordinatorIPAddress == connection.CoordinatorIPAddress;
|
||||||
|
|
||||||
if (sameRef && sameCoordinator)
|
if (sameRef && sameCoordinator)
|
||||||
{
|
|
||||||
throw new ConnectionExists(connection);
|
throw new ConnectionExists(connection);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// CA-15633: XenCenter does not allow connection to host
|
|
||||||
// on which backup is restored.
|
|
||||||
|
|
||||||
throw new BadRestoreDetected(connection);
|
// CA-15633: XenCenter does not allow connection to host on which backup is restored.
|
||||||
}
|
throw new BadRestoreDetected(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
task.Connected = true;
|
task.Connected = true;
|
||||||
@ -1292,39 +1253,10 @@ namespace XenAdmin.Network
|
|||||||
: task.Hostname;
|
: task.Hostname;
|
||||||
} // ConnectionsLock
|
} // ConnectionsLock
|
||||||
|
|
||||||
// Remove any other (disconnected) entries for this server from the tree
|
|
||||||
List<IXenConnection> existingConnections = new List<IXenConnection>();
|
|
||||||
foreach (IXenConnection connection in ConnectionsManager.XenConnections)
|
|
||||||
{
|
|
||||||
if (connection.Hostname.Equals(task.Hostname) && !connection.IsConnected)
|
|
||||||
{
|
|
||||||
existingConnections.Add(connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (IXenConnection connection in existingConnections)
|
|
||||||
{
|
|
||||||
ConnectionsManager.ClearCacheAndRemoveConnection(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
log.DebugFormat("Getting server time for pool {0} ({1})...", FriendlyName, PoolOpaqueRef);
|
log.DebugFormat("Getting server time for pool {0} ({1})...", FriendlyName, PoolOpaqueRef);
|
||||||
SetServerTimeOffset(session, pool.master.opaque_ref);
|
SetServerTimeOffset(session, pool.master.opaque_ref);
|
||||||
|
|
||||||
// add server name to history (if it's not already there)
|
HandleSuccessfulConnection(task, pool);
|
||||||
XenAdminConfigManager.Provider.UpdateServerHistory(HostnameWithPort);
|
|
||||||
|
|
||||||
HandleSuccessfulConnection(task.Hostname, task.Port);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SetPoolAndHostInAction(ConnectAction, pool, PoolOpaqueRef);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
log.Error(e, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.DebugFormat("Completed connection phase for pool {0} ({1}).", FriendlyName, PoolOpaqueRef);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EventsPending();
|
EventsPending();
|
||||||
@ -1398,7 +1330,10 @@ namespace XenAdmin.Network
|
|||||||
ClearEventQueue();
|
ClearEventQueue();
|
||||||
|
|
||||||
connectTask = null;
|
connectTask = null;
|
||||||
HandleConnectionTermination();
|
|
||||||
|
// clean up action so we don't stay open forever
|
||||||
|
if (ConnectAction != null)
|
||||||
|
ConnectAction.IsCompleted = true;
|
||||||
|
|
||||||
if (error is ServerNotSupported)
|
if (error is ServerNotSupported)
|
||||||
{
|
{
|
||||||
@ -1617,7 +1552,7 @@ namespace XenAdmin.Network
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (EventNextBlocked || IsSimulatorConnection(Hostname))
|
if (EventNextBlocked || IsSimulatedConnection)
|
||||||
return RECONNECT_SHORT_TIMEOUT_MS;
|
return RECONNECT_SHORT_TIMEOUT_MS;
|
||||||
|
|
||||||
return RECONNECT_HOST_TIMEOUT_MS;
|
return RECONNECT_HOST_TIMEOUT_MS;
|
||||||
@ -1962,6 +1897,7 @@ namespace XenAdmin.Network
|
|||||||
}
|
}
|
||||||
|
|
||||||
private bool disposed;
|
private bool disposed;
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!disposed)
|
if (!disposed)
|
||||||
@ -1980,7 +1916,7 @@ namespace XenAdmin.Network
|
|||||||
BeforeMajorChange = null;
|
BeforeMajorChange = null;
|
||||||
AfterMajorChange = null;
|
AfterMajorChange = null;
|
||||||
XenObjectsUpdated = null;
|
XenObjectsUpdated = null;
|
||||||
TimeSkewUpdated = null;
|
|
||||||
if (ReconnectionTimer != null)
|
if (ReconnectionTimer != null)
|
||||||
ReconnectionTimer.Dispose();
|
ReconnectionTimer.Dispose();
|
||||||
if (cacheUpdateTimer != null)
|
if (cacheUpdateTimer != null)
|
||||||
@ -2002,35 +1938,39 @@ namespace XenAdmin.Network
|
|||||||
|
|
||||||
public class ConnectionExists : DisconnectionException
|
public class ConnectionExists : DisconnectionException
|
||||||
{
|
{
|
||||||
public IXenConnection connection;
|
protected readonly IXenConnection Connection;
|
||||||
|
|
||||||
public ConnectionExists(IXenConnection connection)
|
public ConnectionExists(IXenConnection connection)
|
||||||
{
|
{
|
||||||
this.connection = connection;
|
Connection = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Message
|
public override string Message
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (connection != null)
|
if (Connection == null)
|
||||||
return string.Format(Messages.CONNECTION_EXISTS, connection.Hostname);
|
|
||||||
else
|
|
||||||
return Messages.CONNECTION_EXISTS_NULL;
|
return Messages.CONNECTION_EXISTS_NULL;
|
||||||
|
|
||||||
|
return string.Format(Messages.CONNECTION_EXISTS, Connection.Hostname);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual string GetDialogMessage(IXenConnection _this)
|
public virtual string GetDialogMessage()
|
||||||
{
|
{
|
||||||
Pool p = Helpers.GetPool(connection);
|
if (Connection == null)
|
||||||
if (p == null)
|
return Messages.CONNECTION_EXISTS_NULL;
|
||||||
return String.Format(Messages.ALREADY_CONNECTED, _this.Hostname);
|
|
||||||
|
|
||||||
return String.Format(Messages.SUPPORTER_ALREADY_CONNECTED, _this.Hostname, p.Name());
|
Pool p = Helpers.GetPool(Connection);
|
||||||
|
if (p == null)
|
||||||
|
return string.Format(Messages.ALREADY_CONNECTED, Connection.Hostname);
|
||||||
|
|
||||||
|
return string.Format(Messages.SUPPORTER_ALREADY_CONNECTED, Connection.Hostname, p.Name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BadRestoreDetected : ConnectionExists
|
internal class BadRestoreDetected : ConnectionExists
|
||||||
{
|
{
|
||||||
public BadRestoreDetected(IXenConnection xc)
|
public BadRestoreDetected(IXenConnection xc)
|
||||||
: base(xc)
|
: base(xc)
|
||||||
@ -2041,11 +1981,14 @@ namespace XenAdmin.Network
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return String.Format(Messages.BAD_RESTORE_DETECTED, connection.Name);
|
if (Connection == null)
|
||||||
|
return Messages.CONNECTION_EXISTS_NULL;
|
||||||
|
|
||||||
|
return string.Format(Messages.BAD_RESTORE_DETECTED, Connection.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetDialogMessage(IXenConnection _this)
|
public override string GetDialogMessage()
|
||||||
{
|
{
|
||||||
return Message;
|
return Message;
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,7 @@ namespace XenAPI
|
|||||||
//Sort roles from highest to lowest
|
//Sort roles from highest to lowest
|
||||||
roles.Sort((r1, r2) => r2.CompareTo(r1));
|
roles.Sort((r1, r2) => r2.CompareTo(r1));
|
||||||
//Take the highest role
|
//Take the highest role
|
||||||
return roles[0].FriendlyName();
|
return roles.Count > 0 ? roles[0].FriendlyName() : Messages.UNKNOWN_AD_USER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
|
||||||
namespace XenAPI
|
namespace XenAPI
|
||||||
@ -45,7 +46,7 @@ namespace XenAPI
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static object convertStruct(Type t, Hashtable table)
|
public static object convertStruct(Type t, Hashtable table)
|
||||||
{
|
{
|
||||||
return t.GetConstructor(new Type[] {typeof(Hashtable)}).Invoke(new object[] {table});
|
return t.GetConstructor(new Type[] { typeof(Hashtable) }).Invoke(new object[] { table });
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Type GetXenAPIType(string name)
|
public static Type GetXenAPIType(string name)
|
||||||
@ -55,31 +56,30 @@ namespace XenAPI
|
|||||||
|
|
||||||
public static bool ParseBool(Hashtable table, string key)
|
public static bool ParseBool(Hashtable table, string key)
|
||||||
{
|
{
|
||||||
var val = table[key];
|
bool.TryParse((string)table[key], out var result);
|
||||||
return val == null ? false : (bool)table[key];
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DateTime ParseDateTime(Hashtable table, string key)
|
public static DateTime ParseDateTime(Hashtable table, string key)
|
||||||
{
|
{
|
||||||
var val = table[key];
|
DateTime.TryParse((string)table[key], out var result);
|
||||||
return val == null ? DateTime.MinValue : (DateTime)table[key];
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double ParseDouble(Hashtable table, string key)
|
public static double ParseDouble(Hashtable table, string key)
|
||||||
{
|
{
|
||||||
var val = table[key];
|
double.TryParse((string)table[key], out var result);
|
||||||
return val == null ? 0.0 : (double)table[key];
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Hashtable ParseHashTable(Hashtable table, string key)
|
public static Hashtable ParseHashTable(Hashtable table, string key)
|
||||||
{
|
{
|
||||||
return (Hashtable)table[key];
|
return ParseSxpDict((string)table[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long ParseLong(Hashtable table, string key)
|
public static long ParseLong(Hashtable table, string key)
|
||||||
{
|
{
|
||||||
long result;
|
long.TryParse((string)table[key], out var result);
|
||||||
long.TryParse((string)table[key], out result);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,14 +90,12 @@ namespace XenAPI
|
|||||||
|
|
||||||
public static string[] ParseStringArray(Hashtable table, string key)
|
public static string[] ParseStringArray(Hashtable table, string key)
|
||||||
{
|
{
|
||||||
var val = (object[])table[key];
|
return ParseSxpList((string)table[key]).ToArray();
|
||||||
return val == null ? new string[0] : Array.ConvertAll(val, o => o.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long[] ParseLongArray(Hashtable table, string key)
|
public static long[] ParseLongArray(Hashtable table, string key)
|
||||||
{
|
{
|
||||||
var val = (object[])table[key];
|
return ParseSxpList((string)table[key]).Select(long.Parse).ToArray();
|
||||||
return val == null ? new long[0] : Array.ConvertAll(val, o => long.Parse(o.ToString()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static XenRef<T> ParseRef<T>(Hashtable table, string key) where T : XenObject<T>
|
public static XenRef<T> ParseRef<T>(Hashtable table, string key) where T : XenObject<T>
|
||||||
@ -108,14 +106,85 @@ namespace XenAPI
|
|||||||
|
|
||||||
public static List<XenRef<T>> ParseSetRef<T>(Hashtable table, string key) where T : XenObject<T>
|
public static List<XenRef<T>> ParseSetRef<T>(Hashtable table, string key) where T : XenObject<T>
|
||||||
{
|
{
|
||||||
var rs = (object[])table[key];
|
return ParseSxpList((string)table[key]).Select(XenRef<T>.Create).ToList();
|
||||||
return rs == null ? null : XenRef<T>.Create(rs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Dictionary<XenRef<T>, T> ParseMapRefRecord<T, U>(Hashtable table, string key) where T : XenObject<T>
|
|
||||||
|
private static Hashtable ParseSxpDict(string p)
|
||||||
{
|
{
|
||||||
Hashtable map = ParseHashTable(table, key);
|
var result = new Hashtable();
|
||||||
return map == null ? null : XenRef<T>.Create<U>(map);
|
|
||||||
|
using (var enumerator = Tokenize(p).GetEnumerator())
|
||||||
|
{
|
||||||
|
if (!enumerator.MoveNext())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
if (enumerator.Current == ")")
|
||||||
|
break;
|
||||||
|
|
||||||
|
enumerator.MoveNext();
|
||||||
|
var key = enumerator.Current;
|
||||||
|
enumerator.MoveNext();
|
||||||
|
var value = enumerator.Current;
|
||||||
|
enumerator.MoveNext();
|
||||||
|
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> ParseSxpList(string p)
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
|
||||||
|
foreach (var token in Tokenize(p))
|
||||||
|
{
|
||||||
|
if (token == "(" || token == ")")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
result.Add(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> Tokenize(string str)
|
||||||
|
{
|
||||||
|
bool inStr = false;
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < str.Length; i++)
|
||||||
|
{
|
||||||
|
switch (str[i])
|
||||||
|
{
|
||||||
|
case '(':
|
||||||
|
if (!inStr)
|
||||||
|
yield return "(";
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
if (!inStr)
|
||||||
|
yield return ")";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '\'':
|
||||||
|
case '"':
|
||||||
|
if (!inStr)
|
||||||
|
{
|
||||||
|
inStr = true;
|
||||||
|
j = i;
|
||||||
|
}
|
||||||
|
else if (str[i - 1] != '\\')
|
||||||
|
{
|
||||||
|
inStr = false;
|
||||||
|
yield return str.Substring(j + 1, i - j - 1).Replace("\\\"", "\"").Replace("\\\'", "\'");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -532,6 +532,7 @@
|
|||||||
<Compile Include="XenAPI\XenObject.cs" />
|
<Compile Include="XenAPI\XenObject.cs" />
|
||||||
<Compile Include="XenObjectDownloader.cs" />
|
<Compile Include="XenObjectDownloader.cs" />
|
||||||
<Compile Include="XenAPI\XenRef.cs" />
|
<Compile Include="XenAPI\XenRef.cs" />
|
||||||
|
<Compile Include="Db.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="..\Branding\Branding.ja.resx">
|
<EmbeddedResource Include="..\Branding\Branding.ja.resx">
|
||||||
|
Loading…
Reference in New Issue
Block a user