mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-30 07:40:13 +01:00
58b240bcde
This fixes the following problem: When the main window is resized, we set the minimum width of the right side panel (Panel2MinSize) to half the size of the window so that the tab contents are visible. This is fine on most situations, but on a higher screen resolution (e.g. 1920x1080) the following problem occurs: - starting with the main window at minimum size, when the window is maximized, the Panel2MinSize is set to half window's size (e.g. 960) - when the main window is restored back to the minimum size, the split container is not resized because there is a conflict between Panel2MinSize and the size of the window itself (Panel1MinSize + Panel2MinSize > window's width) - therefore the navigation panel is not visible and also the content of the tab control may appear out of scale. To fix this, we set the Panel2MinSize to half of the window's minimum width. This way the right side panel will always fit in the window, including when it is restored to minimum size. This also fixes [CA-137956] - Restoring Maximised XenCenter does not resize the main panel properly. Signed-off-by: Mihaela Stoica <mihaela.stoica@citrix.com>
3107 lines
127 KiB
C#
3107 lines
127 KiB
C#
/* Copyright (c) Citrix Systems Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms,
|
|
* with or without modification, are permitted provided
|
|
* that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above
|
|
* copyright notice, this list of conditions and the
|
|
* following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the
|
|
* following disclaimer in the documentation and/or other
|
|
* materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Configuration;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Permissions;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using System.Xml;
|
|
using XenAdmin.Actions.GUIActions;
|
|
using XenAdmin.Controls.MainWindowControls;
|
|
using XenAdmin.Wizards.ImportWizard;
|
|
using XenAPI;
|
|
using XenAdmin.Actions;
|
|
using XenAdmin.Alerts;
|
|
using XenAdmin.Commands;
|
|
using XenAdmin.Controls;
|
|
using XenAdmin.Core;
|
|
using XenAdmin.Dialogs;
|
|
using XenAdmin.Model;
|
|
using XenAdmin.Network;
|
|
using XenAdmin.TabPages;
|
|
using XenAdmin.XenSearch;
|
|
using XenAdmin.Wizards.PatchingWizard;
|
|
using XenAdmin.Plugins;
|
|
using XenAdmin.Network.StorageLink;
|
|
|
|
using System.Linq;
|
|
|
|
namespace XenAdmin
|
|
{
|
|
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
|
|
[ComVisibleAttribute(true)]
|
|
public partial class MainWindow : Form, ISynchronizeInvoke, IMainWindow
|
|
{
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
/// <summary>
|
|
/// A mapping between objects in the tree and the associated selected tab.
|
|
/// </summary>
|
|
private Dictionary<object, TabPage> selectedTabs = new Dictionary<object, TabPage>();
|
|
|
|
/// <summary>
|
|
/// The selected tab for the overview node.
|
|
/// </summary>
|
|
private TabPage selectedOverviewTab = null;
|
|
|
|
internal readonly PerformancePage PerformancePage = new PerformancePage();
|
|
internal readonly GeneralTabPage GeneralPage = new GeneralTabPage();
|
|
internal readonly BallooningPage BallooningPage = new BallooningPage();
|
|
internal readonly BallooningUpsellPage BallooningUpsellPage = new BallooningUpsellPage();
|
|
internal readonly ConsolePanel ConsolePanel = new ConsolePanel();
|
|
internal readonly HAPage HAPage = new HAPage();
|
|
internal readonly HAUpsellPage HAUpsellPage = new HAUpsellPage();
|
|
internal readonly HomePage HomePage = new HomePage();
|
|
internal readonly SearchPage SearchPage = new SearchPage();
|
|
internal readonly NetworkPage NetworkPage = new NetworkPage();
|
|
internal readonly NICPage NICPage = new NICPage();
|
|
internal readonly WlbPage WlbPage = new WlbPage();
|
|
internal readonly WLBUpsellPage WLBUpsellPage = new WLBUpsellPage();
|
|
internal readonly SrStoragePage SrStoragePage = new SrStoragePage();
|
|
internal readonly PhysicalStoragePage PhysicalStoragePage = new PhysicalStoragePage();
|
|
internal readonly VMStoragePage VMStoragePage = new VMStoragePage();
|
|
internal readonly AdPage AdPage = new AdPage();
|
|
internal readonly GpuPage GpuPage = new GpuPage();
|
|
|
|
private ActionBase statusBarAction = null;
|
|
public ActionBase StatusBarAction { get { return statusBarAction; } }
|
|
|
|
private bool IgnoreTabChanges = false;
|
|
private bool ToolbarsEnabled;
|
|
|
|
private readonly Dictionary<IXenConnection, IList<Form>> activePoolWizards = new Dictionary<IXenConnection, IList<Form>>();
|
|
private readonly Dictionary<IXenObject, Form> activeXenModelObjectWizards = new Dictionary<IXenObject, Form>();
|
|
|
|
/// <summary>
|
|
/// The arguments passed in on the command line.
|
|
/// </summary>
|
|
private string[] CommandLineParam = null;
|
|
private ArgType CommandLineArgType = ArgType.None;
|
|
|
|
private static readonly System.Windows.Forms.Timer CheckForUpdatesTimer = new System.Windows.Forms.Timer();
|
|
|
|
private readonly PluginManager pluginManager;
|
|
private readonly ContextMenuBuilder contextMenuBuilder;
|
|
|
|
private readonly LicenseManagerLauncher licenseManagerLauncher;
|
|
private readonly LicenseTimer licenseTimer;
|
|
|
|
private Dictionary<ToolStripMenuItem, int> pluginMenuItemStartIndexes = new Dictionary<ToolStripMenuItem, int>();
|
|
|
|
public MainWindow(ArgType argType, string[] args)
|
|
{
|
|
Program.MainWindow = this;
|
|
licenseManagerLauncher = new LicenseManagerLauncher(Program.MainWindow);
|
|
InvokeHelper.Initialize(this);
|
|
|
|
InitializeComponent();
|
|
SetMenuItemStartIndexes();
|
|
Icon = Properties.Resources.AppIcon;
|
|
|
|
#region Add Tab pages
|
|
|
|
components.Add(NICPage);
|
|
components.Add(VMStoragePage);
|
|
components.Add(SrStoragePage);
|
|
components.Add(PerformancePage);
|
|
components.Add(GeneralPage);
|
|
components.Add(BallooningPage);
|
|
components.Add(ConsolePanel);
|
|
components.Add(NetworkPage);
|
|
components.Add(HAPage);
|
|
components.Add(HomePage);
|
|
components.Add(WlbPage);
|
|
components.Add(AdPage);
|
|
components.Add(GpuPage);
|
|
components.Add(SearchPage);
|
|
|
|
AddTabContents(VMStoragePage, TabPageStorage);
|
|
AddTabContents(SrStoragePage, TabPageSR);
|
|
AddTabContents(NICPage, TabPageNICs);
|
|
AddTabContents(PerformancePage, TabPagePeformance);
|
|
AddTabContents(GeneralPage, TabPageGeneral);
|
|
AddTabContents(BallooningPage, TabPageBallooning);
|
|
AddTabContents(BallooningUpsellPage, TabPageBallooningUpsell);
|
|
AddTabContents(ConsolePanel, TabPageConsole);
|
|
AddTabContents(NetworkPage, TabPageNetwork);
|
|
AddTabContents(HAPage, TabPageHA);
|
|
AddTabContents(HAUpsellPage, TabPageHAUpsell);
|
|
AddTabContents(HomePage, TabPageHome);
|
|
AddTabContents(WlbPage, TabPageWLB);
|
|
AddTabContents(WLBUpsellPage, TabPageWLBUpsell);
|
|
AddTabContents(PhysicalStoragePage, TabPagePhysicalStorage);
|
|
AddTabContents(AdPage, TabPageAD);
|
|
AddTabContents(GpuPage, TabPageGPU);
|
|
AddTabContents(SearchPage, TabPageSearch);
|
|
|
|
#endregion
|
|
|
|
TheTabControl.SelectedIndexChanged += TheTabControl_SelectedIndexChanged;
|
|
navigationPane.DragDropCommandActivated += navigationPane_DragDropCommandActivated;
|
|
|
|
PoolCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged<Pool>);
|
|
MessageCollectionChangedWithInvoke = Program.ProgramInvokeHandler(MessageCollectionChanged);
|
|
HostCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged<Host>);
|
|
VMCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged<VM>);
|
|
SRCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged<SR>);
|
|
FolderCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged<Folder>);
|
|
TaskCollectionChangedWithInvoke = Program.ProgramInvokeHandler(MeddlingActionManager.TaskCollectionChanged);
|
|
ConnectionsManager.History.CollectionChanged += History_CollectionChanged;
|
|
|
|
CommandLineArgType = argType;
|
|
CommandLineParam = args;
|
|
|
|
pluginManager = new PluginManager();
|
|
pluginManager.PluginsChanged += pluginManager_PluginsChanged;
|
|
pluginManager.LoadPlugins();
|
|
contextMenuBuilder = new ContextMenuBuilder(pluginManager, this);
|
|
|
|
eventsPage.GoToXenObjectRequested += eventsPage_GoToXenObjectRequested;
|
|
SearchPage.SearchChanged += SearchPanel_SearchChanged;
|
|
Alert.RegisterAlertCollectionChanged(XenCenterAlerts_CollectionChanged);
|
|
Updates.RegisterCollectionChanged(Updates_CollectionChanged);
|
|
|
|
FormFontFixer.Fix(this);
|
|
|
|
Folders.InitFolders();
|
|
OtherConfigAndTagsWatcher.InitEventHandlers();
|
|
|
|
// Fix colour of text on gradient panels
|
|
TitleLabel.ForeColor = Program.TitleBarForeColor;
|
|
loggedInLabel1.SetTextColor(Program.TitleBarForeColor);
|
|
|
|
statusProgressBar.Visible = false;
|
|
|
|
SelectionManager.BindTo(MainMenuBar.Items, this);
|
|
SelectionManager.BindTo(ToolStrip.Items, this);
|
|
Properties.Settings.Default.SettingChanging += Default_SettingChanging;
|
|
|
|
licenseTimer = new LicenseTimer(licenseManagerLauncher);
|
|
GeneralPage.LicenseLauncher = licenseManagerLauncher;
|
|
}
|
|
|
|
private void Default_SettingChanging(object sender, SettingChangingEventArgs e)
|
|
{
|
|
if (e == null)
|
|
return;
|
|
|
|
if (e.SettingName == "AutoSwitchToRDP" || e.SettingName == "EnableRDPPolling")
|
|
{
|
|
ConsolePanel.ResetAllViews();
|
|
|
|
if (SelectionManager.Selection.FirstIsRealVM)
|
|
ConsolePanel.setCurrentSource((VM)SelectionManager.Selection.First);
|
|
else if (SelectionManager.Selection.FirstIsHost)
|
|
ConsolePanel.setCurrentSource((Host)SelectionManager.Selection.First);
|
|
|
|
UnpauseVNC(sender == TheTabControl);
|
|
}
|
|
}
|
|
|
|
private void SetMenuItemStartIndexes()
|
|
{
|
|
foreach (ToolStripMenuItem menu in MainMenuBar.Items)
|
|
{
|
|
foreach (ToolStripItem item in menu.DropDownItems)
|
|
{
|
|
if (item != null && item.Text == "PluginItemsPlaceHolder")
|
|
{
|
|
pluginMenuItemStartIndexes.Add(menu, menu.DropDownItems.IndexOf(item));
|
|
menu.DropDownItems.Remove(item);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal SelectionBroadcaster SelectionManager
|
|
{
|
|
get
|
|
{
|
|
return navigationPane.SelectionManager;
|
|
}
|
|
}
|
|
|
|
internal ContextMenuBuilder ContextMenuBuilder
|
|
{
|
|
get
|
|
{
|
|
return contextMenuBuilder;
|
|
}
|
|
}
|
|
|
|
protected override void OnLoad(EventArgs e)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
|
|
History.EnableHistoryButtons();
|
|
History.NewHistoryItem(new XenModelObjectHistoryItem(null, TabPageHome));
|
|
|
|
/*
|
|
* Resume window size and location
|
|
*/
|
|
try
|
|
{
|
|
// Bring in previous version user setting for the first time.
|
|
if (Properties.Settings.Default.DoUpgrade)
|
|
{
|
|
Properties.Settings.Default.Upgrade();
|
|
Properties.Settings.Default.DoUpgrade = false;
|
|
XenAdmin.Settings.TrySaveSettings();
|
|
}
|
|
|
|
Point savedLocation = Properties.Settings.Default.WindowLocation;
|
|
Size savedSize = Properties.Settings.Default.WindowSize;
|
|
|
|
if (HelpersGUI.WindowIsOnScreen(savedLocation, savedSize))
|
|
{
|
|
this.Location = savedLocation;
|
|
this.Size = savedSize;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
|
|
// Using the Load event ensures that the handle has been
|
|
// created:
|
|
base.OnLoad(e);
|
|
|
|
NewTabs[0] = TabPageHome;
|
|
NewTabCount = 1;
|
|
ChangeToNewTabs();
|
|
}
|
|
|
|
protected override void OnShown(EventArgs e)
|
|
{
|
|
base.OnShown(e);
|
|
TheTabControl.Visible = true;
|
|
alertPage.Visible = updatesPage.Visible = eventsPage.Visible = false;
|
|
navigationPane.FocusTreeView();
|
|
}
|
|
|
|
protected override void OnHandleCreated(EventArgs e)
|
|
{
|
|
base.OnHandleCreated(e);
|
|
Clip.RegisterClipboardViewer();
|
|
}
|
|
|
|
protected override void WndProc(ref System.Windows.Forms.Message e)
|
|
{
|
|
//System.Console.WriteLine(Win32.GetWindowsMessageName(e.Msg));
|
|
switch (e.Msg)
|
|
{
|
|
case Win32.WM_CHANGECBCHAIN: // Clipboard chain has changed.
|
|
Clip.ProcessWMChangeCBChain(e);
|
|
break;
|
|
|
|
case Win32.WM_DRAWCLIPBOARD: // Content of clipboard has changed.
|
|
Clip.ProcessWMDrawClipboard(e);
|
|
break;
|
|
|
|
case Win32.WM_DESTROY:
|
|
Clip.UnregisterClipboardViewer();
|
|
base.WndProc(ref e);
|
|
break;
|
|
|
|
default:
|
|
base.WndProc(ref e);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void AddTabContents(Control contents, TabPage TabPage)
|
|
{
|
|
contents.Location = new Point(0, 0);
|
|
contents.Size = TabPage.Size;
|
|
contents.Anchor = AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Right;
|
|
TabPage.Controls.Add(contents);
|
|
}
|
|
|
|
void History_CollectionChanged(object sender, CollectionChangeEventArgs e)
|
|
{
|
|
if (Program.Exiting)
|
|
return;
|
|
|
|
Program.BeginInvoke(Program.MainWindow, () =>
|
|
{
|
|
ActionBase action = (ActionBase)e.Element;
|
|
if (action == null)
|
|
return;
|
|
|
|
switch (e.Action)
|
|
{
|
|
case CollectionChangeAction.Add:
|
|
{
|
|
var meddlingAction = action as MeddlingAction;
|
|
if (meddlingAction == null)
|
|
{
|
|
SetStatusBar(null, null);
|
|
if (statusBarAction != null)
|
|
{
|
|
statusBarAction.Changed -= actionChanged;
|
|
statusBarAction.Completed -= actionChanged;
|
|
}
|
|
statusBarAction = action;
|
|
}
|
|
action.Changed += actionChanged;
|
|
action.Completed += actionChanged;
|
|
actionChanged(action);
|
|
break;
|
|
}
|
|
case CollectionChangeAction.Remove:
|
|
{
|
|
action.Changed -= actionChanged;
|
|
action.Completed -= actionChanged;
|
|
|
|
int errors = ConnectionsManager.History.Count(a => a.IsCompleted && !a.Succeeded);
|
|
navigationPane.UpdateNotificationsButton(NotificationsSubMode.Events, errors);
|
|
|
|
if (eventsPage.Visible)
|
|
{
|
|
TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Events, errors);
|
|
TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Events, errors);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void actionChanged(ActionBase action)
|
|
{
|
|
if (Program.Exiting)
|
|
return;
|
|
|
|
Program.Invoke(this, () => actionChanged_(action));
|
|
}
|
|
|
|
void actionChanged_(ActionBase action)
|
|
{
|
|
var meddlingAction = action as MeddlingAction;
|
|
if (meddlingAction == null)
|
|
statusProgressBar.Visible = action.ShowProgress && !action.IsCompleted;
|
|
|
|
// Be defensive against CA-8517.
|
|
if (action.PercentComplete < 0 || action.PercentComplete > 100)
|
|
{
|
|
log.ErrorFormat("PercentComplete is erroneously {0}", action.PercentComplete);
|
|
}
|
|
else if (meddlingAction == null)
|
|
{
|
|
statusProgressBar.Value = action.PercentComplete;
|
|
}
|
|
|
|
// Don't show cancelled exception
|
|
if (action.Exception != null && !(action.Exception is CancelledException))
|
|
{
|
|
if (meddlingAction == null)
|
|
SetStatusBar(Properties.Resources._000_error_h32bit_16, action.Exception.Message);
|
|
|
|
IXenObject model =
|
|
(IXenObject)action.VM ??
|
|
(IXenObject)action.Host ??
|
|
(IXenObject)action.Pool ??
|
|
(IXenObject)action.SR;
|
|
if (model != null)
|
|
model.InError = true;
|
|
|
|
RequestRefreshTreeView();
|
|
}
|
|
else if (meddlingAction == null)
|
|
{
|
|
SetStatusBar(null, action.IsCompleted
|
|
? null
|
|
: !string.IsNullOrEmpty(action.Description)
|
|
? action.Description
|
|
: !string.IsNullOrEmpty(action.Title)
|
|
? action.Title
|
|
: null);
|
|
}
|
|
|
|
int errors = ConnectionsManager.History.Count(a => a.IsCompleted && !a.Succeeded);
|
|
navigationPane.UpdateNotificationsButton(NotificationsSubMode.Events, errors);
|
|
|
|
if (eventsPage.Visible)
|
|
{
|
|
TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Events, errors);
|
|
TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Events, errors);
|
|
}
|
|
}
|
|
|
|
public void SetStatusBar(Image image, string message)
|
|
{
|
|
if ((statusLabel.Text != null && statusLabel.Text.Equals(message))
|
|
|| (statusLabel.Image != null && statusLabel.Equals(image)))
|
|
return;
|
|
|
|
statusLabel.Image = image;
|
|
statusLabel.Text = Helpers.FirstLine(message);
|
|
}
|
|
|
|
public void SetProgressBar(bool visible, int progress)
|
|
{
|
|
statusProgressBar.Visible = visible;
|
|
statusProgressBar.Value = progress;
|
|
}
|
|
|
|
private void MainWindow_Shown(object sender, EventArgs e)
|
|
{
|
|
MainMenuBar.Location = new Point(0, 0);
|
|
|
|
if (ToolStrip.Renderer is ToolStripProfessionalRenderer)
|
|
{
|
|
((ToolStripProfessionalRenderer)ToolStrip.Renderer).RoundedEdges = false;
|
|
}
|
|
|
|
ConnectionsManager.XenConnections.CollectionChanged += XenConnection_CollectionChanged;
|
|
try
|
|
{
|
|
Settings.RestoreSession();
|
|
}
|
|
catch (ConfigurationErrorsException ex)
|
|
{
|
|
log.Error("Could not load settings.", ex);
|
|
Program.CloseSplash();
|
|
new ThreeButtonDialog(
|
|
new ThreeButtonDialog.Details(
|
|
SystemIcons.Error,
|
|
string.Format(Messages.MESSAGEBOX_LOAD_CORRUPTED, Settings.GetUserConfigPath()),
|
|
Messages.MESSAGEBOX_LOAD_CORRUPTED_TITLE)).ShowDialog(this);
|
|
Application.Exit();
|
|
return; // Application.Exit() does not exit the current method.
|
|
}
|
|
ToolbarsEnabled = Properties.Settings.Default.ToolbarsEnabled;
|
|
RequestRefreshTreeView();
|
|
UpdateToolbars();
|
|
|
|
// kick-off connections for all the loaded server list
|
|
foreach (IXenConnection connection in ConnectionsManager.XenConnectionsCopy)
|
|
{
|
|
if (!connection.SaveDisconnected)
|
|
{
|
|
XenConnectionUI.BeginConnect(connection, true, this, true);
|
|
|
|
// if there are fewer than 30 connections, then expand the tree nodes.
|
|
if (ConnectionsManager.XenConnectionsCopy.Count < 30)
|
|
{
|
|
connection.CachePopulated += connection_CachePopulatedOnStartup;
|
|
}
|
|
}
|
|
}
|
|
|
|
Program.StorageLinkConnections.CollectionChanged += StorageLinkConnections_CollectionChanged;
|
|
|
|
RequestRefreshTreeView();
|
|
|
|
ThreadPool.QueueUserWorkItem((WaitCallback)delegate(object o)
|
|
{
|
|
// Sleep a short time before closing the splash
|
|
Thread.Sleep(500);
|
|
Program.Invoke(Program.MainWindow, Program.CloseSplash);
|
|
});
|
|
|
|
if (!Program.RunInAutomatedTestMode && !Helpers.CommonCriteriaCertificationRelease)
|
|
{
|
|
if (!Properties.Settings.Default.SeenAllowUpdatesDialog)
|
|
new AllowUpdatesDialog(pluginManager).ShowDialog(this);
|
|
|
|
// start checkforupdates thread
|
|
CheckForUpdatesTimer.Interval = 1000 * 60 * 60 * 24; // 24 hours
|
|
CheckForUpdatesTimer.Tick += CheckForUpdatesTimer_Tick;
|
|
CheckForUpdatesTimer.Start();
|
|
Updates.CheckForUpdates(false);
|
|
}
|
|
ProcessCommand(CommandLineArgType, CommandLineParam);
|
|
}
|
|
|
|
private void CheckForUpdatesTimer_Tick(object sender, EventArgs e)
|
|
{
|
|
Updates.CheckForUpdates(false);
|
|
}
|
|
|
|
private void LoadTasksAsMeddlingActions(IXenConnection connection)
|
|
{
|
|
if (!connection.IsConnected || connection.Session == null)
|
|
return;
|
|
|
|
Dictionary<XenRef<Task>, Task> tasks = Task.get_all_records(connection.Session);
|
|
foreach (KeyValuePair<XenRef<Task>, Task> pair in tasks)
|
|
{
|
|
pair.Value.Connection = connection;
|
|
pair.Value.opaque_ref = pair.Key;
|
|
MeddlingActionManager.ForceAddTask(pair.Value);
|
|
}
|
|
}
|
|
|
|
private void StorageLinkConnections_CollectionChanged(object sender, CollectionChangeEventArgs e)
|
|
{
|
|
var con = ((StorageLinkConnection)e.Element);
|
|
|
|
if (e.Action == CollectionChangeAction.Add)
|
|
{
|
|
con.ConnectionStateChanged += (s, ee) =>
|
|
{
|
|
if (ee.ConnectionState == StorageLinkConnectionState.Connected)
|
|
{
|
|
RequestRefreshTreeView();
|
|
|
|
TrySelectNewNode(o =>
|
|
{
|
|
var server = con.Cache.Server;
|
|
return server != null && server.Equals(o);
|
|
}, false, true, false);
|
|
|
|
}
|
|
Program.Invoke(this, UpdateToolbars);
|
|
};
|
|
|
|
con.Cache.Changed += Cache_Changed;
|
|
}
|
|
else if (e.Action == CollectionChangeAction.Remove)
|
|
{
|
|
con.Cache.Changed -= Cache_Changed;
|
|
}
|
|
RequestRefreshTreeView();
|
|
}
|
|
|
|
private void Cache_Changed(object sender, EventArgs e)
|
|
{
|
|
RequestRefreshTreeView();
|
|
}
|
|
|
|
private void connection_CachePopulatedOnStartup(object sender, EventArgs e)
|
|
{
|
|
IXenConnection c = (IXenConnection)sender;
|
|
c.CachePopulated -= connection_CachePopulatedOnStartup;
|
|
TrySelectNewNode(c, false, true, false);
|
|
}
|
|
|
|
private bool Launched = false;
|
|
internal void ProcessCommand(ArgType argType, string[] args)
|
|
{
|
|
switch (argType)
|
|
{
|
|
case ArgType.Import:
|
|
log.DebugFormat("Importing VM export from {0}", args[0]);
|
|
OpenGlobalImportWizard(args[0]);
|
|
break;
|
|
case ArgType.License:
|
|
log.DebugFormat("Installing license from {0}", args[0]);
|
|
LaunchLicensePicker(args[0]);
|
|
break;
|
|
case ArgType.Restore:
|
|
log.DebugFormat("Restoring host backup from {0}", args[0]);
|
|
new RestoreHostFromBackupCommand(this, null, args[0]).Execute();
|
|
break;
|
|
case ArgType.Update:
|
|
log.DebugFormat("Installing server update from {0}", args[0]);
|
|
InstallUpdate(args[0]);
|
|
break;
|
|
case ArgType.XenSearch:
|
|
log.DebugFormat("Importing saved XenSearch from '{0}'", args[0]);
|
|
new ImportSearchCommand(this, args[0]).Execute();
|
|
break;
|
|
case ArgType.Connect:
|
|
log.DebugFormat("Connecting to server '{0}'", args[0]);
|
|
IXenConnection connection = new XenConnection();
|
|
connection.Hostname = args[0];
|
|
connection.Port = ConnectionsManager.DEFAULT_XEN_PORT;
|
|
connection.Username = args[1];
|
|
connection.Password = args[2];
|
|
if (ConnectionsManager.XenConnectionsContains(connection))
|
|
break;
|
|
|
|
lock (ConnectionsManager.ConnectionsLock)
|
|
ConnectionsManager.XenConnections.Add(connection);
|
|
|
|
XenConnectionUI.BeginConnect(connection, true, null, false);
|
|
break;
|
|
case ArgType.None:
|
|
if (Launched)
|
|
{
|
|
// The user has launched the splash screen, but we're already running.
|
|
// Draw his attention.
|
|
HelpersGUI.BringFormToFront(this);
|
|
Activate();
|
|
}
|
|
break;
|
|
case ArgType.Passwords:
|
|
System.Diagnostics.Trace.Assert(false);
|
|
break;
|
|
}
|
|
Launched = true;
|
|
}
|
|
|
|
// Manages UI and network updates whenever hosts are added and removed
|
|
void XenConnection_CollectionChanged(object sender, CollectionChangeEventArgs e)
|
|
{
|
|
if (Program.Exiting)
|
|
return;
|
|
|
|
//Program.AssertOnEventThread();
|
|
Program.BeginInvoke(Program.MainWindow, () => XenConnectionCollectionChanged(e));
|
|
}
|
|
|
|
|
|
private readonly CollectionChangeEventHandler PoolCollectionChangedWithInvoke = null;
|
|
private readonly CollectionChangeEventHandler MessageCollectionChangedWithInvoke = null;
|
|
private readonly CollectionChangeEventHandler HostCollectionChangedWithInvoke = null;
|
|
private readonly CollectionChangeEventHandler VMCollectionChangedWithInvoke = null;
|
|
private readonly CollectionChangeEventHandler SRCollectionChangedWithInvoke = null;
|
|
private readonly CollectionChangeEventHandler FolderCollectionChangedWithInvoke = null;
|
|
private readonly CollectionChangeEventHandler TaskCollectionChangedWithInvoke = null;
|
|
|
|
private void XenConnectionCollectionChanged(CollectionChangeEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
IXenConnection connection = (IXenConnection)e.Element;
|
|
if (connection == null)
|
|
return;
|
|
|
|
navigationPane.XenConnectionCollectionChanged(e);
|
|
|
|
if (e.Action == CollectionChangeAction.Add)
|
|
{
|
|
connection.ClearingCache += connection_ClearingCache;
|
|
connection.ConnectionResult += Connection_ConnectionResult;
|
|
connection.ConnectionLost += Connection_ConnectionLost;
|
|
connection.ConnectionClosed += Connection_ConnectionClosed;
|
|
connection.ConnectionReconnecting += connection_ConnectionReconnecting;
|
|
connection.XenObjectsUpdated += Connection_XenObjectsUpdated;
|
|
connection.Cache.RegisterCollectionChanged<XenAPI.Message>(MessageCollectionChangedWithInvoke);
|
|
connection.Cache.RegisterCollectionChanged<Pool>(PoolCollectionChangedWithInvoke);
|
|
connection.Cache.RegisterCollectionChanged<Host>(HostCollectionChangedWithInvoke);
|
|
connection.Cache.RegisterCollectionChanged<VM>(VMCollectionChangedWithInvoke);
|
|
connection.Cache.RegisterCollectionChanged<SR>(SRCollectionChangedWithInvoke);
|
|
connection.Cache.RegisterCollectionChanged<Folder>(FolderCollectionChangedWithInvoke);
|
|
|
|
connection.Cache.RegisterCollectionChanged<Task>(TaskCollectionChangedWithInvoke);
|
|
|
|
connection.CachePopulated += connection_CachePopulated;
|
|
}
|
|
else if (e.Action == CollectionChangeAction.Remove)
|
|
{
|
|
connection.ClearingCache -= connection_ClearingCache;
|
|
connection.ConnectionResult -= Connection_ConnectionResult;
|
|
connection.ConnectionLost -= Connection_ConnectionLost;
|
|
connection.ConnectionClosed -= Connection_ConnectionClosed;
|
|
connection.ConnectionReconnecting -= connection_ConnectionReconnecting;
|
|
connection.XenObjectsUpdated -= Connection_XenObjectsUpdated;
|
|
connection.Cache.DeregisterCollectionChanged<XenAPI.Message>(MessageCollectionChangedWithInvoke);
|
|
connection.Cache.DeregisterCollectionChanged<Pool>(PoolCollectionChangedWithInvoke);
|
|
connection.Cache.DeregisterCollectionChanged<Host>(HostCollectionChangedWithInvoke);
|
|
connection.Cache.DeregisterCollectionChanged<VM>(VMCollectionChangedWithInvoke);
|
|
connection.Cache.DeregisterCollectionChanged<SR>(SRCollectionChangedWithInvoke);
|
|
connection.Cache.DeregisterCollectionChanged<Folder>(FolderCollectionChangedWithInvoke);
|
|
|
|
connection.Cache.DeregisterCollectionChanged<Task>(TaskCollectionChangedWithInvoke);
|
|
|
|
connection.CachePopulated -= connection_CachePopulated;
|
|
|
|
foreach (VM vm in connection.Cache.VMs)
|
|
{
|
|
this.ConsolePanel.closeVNCForSource(vm);
|
|
}
|
|
|
|
foreach (Host host in connection.Cache.Hosts)
|
|
foreach (VM vm in host.Connection.ResolveAll(host.resident_VMs))
|
|
if (vm.is_control_domain)
|
|
this.ConsolePanel.closeVNCForSource(vm);
|
|
|
|
connection.EndConnect();
|
|
|
|
RequestRefreshTreeView();
|
|
//CA-41228 refresh submenu items when there are no connections
|
|
SelectionManager.RefreshSelection();
|
|
}
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Error(exn, exn);
|
|
// Can't do any more about this.
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Closes any wizards for this connection. Must be done before we clear the cache so that per-VM wizards are closed.
|
|
/// In many cases this is already covered (e.g. if the user explicitly disconnects). This method ensures we also
|
|
/// do it when we unexpectedly lose the connection.
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
private void connection_ClearingCache(object sender, EventArgs e)
|
|
{
|
|
IXenConnection connection = (IXenConnection)sender;
|
|
CloseActiveWizards(connection);
|
|
Alert.RemoveAlert(alert => alert.Connection != null && alert.Connection.Equals(connection));
|
|
Updates.CheckServerPatches();
|
|
Updates.CheckServerVersion();
|
|
}
|
|
|
|
void connection_CachePopulated(object sender, EventArgs e)
|
|
{
|
|
IXenConnection connection = sender as IXenConnection;
|
|
if (connection == null)
|
|
return;
|
|
|
|
Host master = Helpers.GetMaster(connection);
|
|
if (master == null)
|
|
return;
|
|
|
|
log.InfoFormat("Connected to {0} (version {1}, build {2}.{3}) with XenCenter {4} (build {5}.{6})",
|
|
Helpers.GetName(master), Helpers.HostProductVersionText(master), Helpers.HostProductVersion(master),
|
|
Helpers.HostBuildNumber(master), Branding.PRODUCT_VERSION_TEXT,
|
|
Branding.XENCENTER_VERSION, Program.Version.Revision);
|
|
|
|
// When releasing a new version of the server, we should set xencenter_min and xencenter_max on the server
|
|
// as follows:
|
|
//
|
|
// xencenter_min should be the lowest version of XenCenter we want the new server to work with. In the
|
|
// (common) case that we want to force the user to upgrade XenCenter when they upgrade the server,
|
|
// xencenter_min should equal the current version of XenCenter. // if (server_min > current_version)
|
|
//
|
|
// xencenter_max should always equal the current version of XenCenter. This ensures that even if they are
|
|
// not required to upgrade, we at least warn them. // else if (server_max > current_version)
|
|
|
|
int server_min = master.XenCenterMin;
|
|
int server_max = master.XenCenterMax;
|
|
|
|
if (server_min > 0 && server_max > 0)
|
|
{
|
|
int current_version = (int)API_Version.LATEST;
|
|
|
|
if (server_min > current_version)
|
|
{
|
|
connection.EndConnect();
|
|
|
|
Program.Invoke(Program.MainWindow, delegate()
|
|
{
|
|
string msg = string.Format(Messages.GUI_OUT_OF_DATE, Helpers.GetName(master));
|
|
string url = "https://" + connection.Hostname;
|
|
|
|
using (var dlog = new ConnectionRefusedDialog())
|
|
{
|
|
dlog.ErrorMessage = msg;
|
|
dlog.Url = url;
|
|
dlog.ShowDialog(this);
|
|
}
|
|
|
|
new ActionBase(Messages.CONNECTION_REFUSED_TITLE,
|
|
string.Format("{0}\n{1}", msg, url), false,
|
|
true, Messages.CONNECTION_REFUSED);
|
|
});
|
|
return;
|
|
}
|
|
else if (server_max > current_version)
|
|
{
|
|
Alert.AddAlert(new GuiOldAlert());
|
|
}
|
|
|
|
LoadTasksAsMeddlingActions(connection);
|
|
}
|
|
|
|
//
|
|
// Every time we connect, make sure any host with other_config[maintenance_mode] == true
|
|
// is disabled.
|
|
//
|
|
CheckMaintenanceMode(connection);
|
|
|
|
if (HelpersGUI.iSCSIisUsed())
|
|
HelpersGUI.PerformIQNCheck();
|
|
|
|
if(licenseTimer != null)
|
|
licenseTimer.CheckActiveServerLicense(connection, false);
|
|
|
|
Updates.CheckServerPatches();
|
|
Updates.CheckServerVersion();
|
|
RequestRefreshTreeView();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures all hosts on the connection are disabled if they are in maintenance mode.
|
|
/// </summary>
|
|
/// <param name="connection"></param>
|
|
private void CheckMaintenanceMode(IXenConnection connection)
|
|
{
|
|
foreach (Host host in connection.Cache.Hosts)
|
|
{
|
|
CheckMaintenanceMode(host);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures the host is disabled if it is in maintenance mode by spawning a new HostAction if necessary.
|
|
/// </summary>
|
|
/// <param name="host"></param>
|
|
private void CheckMaintenanceMode(Host host)
|
|
{
|
|
if (host.IsLive && host.MaintenanceMode && host.enabled)
|
|
{
|
|
Program.MainWindow.CloseActiveWizards(host);
|
|
|
|
var action = new DisableHostAction(host);
|
|
action.Completed += action_Completed;
|
|
action.RunAsync();
|
|
Program.Invoke(this, UpdateToolbars);
|
|
}
|
|
}
|
|
|
|
void MessageCollectionChanged(object sender, CollectionChangeEventArgs e)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
|
|
XenAPI.Message m = (XenAPI.Message)e.Element;
|
|
if (e.Action == CollectionChangeAction.Add)
|
|
{
|
|
if (!m.ShowOnGraphs && !m.IsSquelched)
|
|
Alert.AddAlert(MessageAlert.ParseMessage(m));
|
|
}
|
|
else if (e.Action == CollectionChangeAction.Remove)
|
|
{
|
|
if (!m.ShowOnGraphs)
|
|
MessageAlert.RemoveAlert(m);
|
|
}
|
|
}
|
|
|
|
void CollectionChanged<T>(object sender, CollectionChangeEventArgs e) where T : XenObject<T>
|
|
{
|
|
Program.AssertOnEventThread();
|
|
|
|
T o = (T)e.Element;
|
|
if (e.Action == CollectionChangeAction.Add)
|
|
{
|
|
if (o is Pool)
|
|
((Pool)e.Element).PropertyChanged += Pool_PropertyChanged;
|
|
else if (o is Host)
|
|
((Host)e.Element).PropertyChanged += Host_PropertyChanged;
|
|
else if (o is VM)
|
|
((VM)e.Element).PropertyChanged += VM_PropertyChanged;
|
|
else
|
|
o.PropertyChanged += o_PropertyChanged;
|
|
}
|
|
else if (e.Action == CollectionChangeAction.Remove)
|
|
{
|
|
if (o is Pool)
|
|
((Pool)e.Element).PropertyChanged -= Pool_PropertyChanged;
|
|
else if (o is Host)
|
|
((Host)e.Element).PropertyChanged -= Host_PropertyChanged;
|
|
else if (o is VM)
|
|
((VM)e.Element).PropertyChanged -= VM_PropertyChanged;
|
|
else
|
|
o.PropertyChanged -= o_PropertyChanged;
|
|
|
|
if (o is VM)
|
|
{
|
|
VM vm = (VM)e.Element;
|
|
ConsolePanel.closeVNCForSource(vm);
|
|
CloseActiveWizards(vm);
|
|
}
|
|
|
|
selectedTabs.Remove(o);
|
|
pluginManager.DisposeURLs(o);
|
|
}
|
|
}
|
|
|
|
private void Pool_PropertyChanged(object obj, PropertyChangedEventArgs e)
|
|
{
|
|
Pool pool = (Pool)obj;
|
|
switch (e.PropertyName)
|
|
{
|
|
case "other_config":
|
|
// other_config may contain HideFromXenCenter
|
|
UpdateToolbars();
|
|
// other_config contains which patches to ignore
|
|
Updates.CheckServerPatches();
|
|
Updates.CheckServerVersion();
|
|
break;
|
|
|
|
case "name_label":
|
|
pool.Connection.FriendlyName = Helpers.GetName(pool);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void Host_PropertyChanged(object obj, PropertyChangedEventArgs e)
|
|
{
|
|
Host host = (Host)obj;
|
|
switch (e.PropertyName)
|
|
{
|
|
case "allowed_operations":
|
|
case "enabled":
|
|
// We want to ensure that a host is disabled if it is in maintenance mode, by starting a new DisableHostAction if necessary (CheckMaintenanceMode)
|
|
if (host.enabled && host.MaintenanceMode)
|
|
{
|
|
// This is an invalid state: the host is enabled but still "in maintenance mode";
|
|
// But maybe MaintenanceMode hasn't been updated yet, because host.enabled being processed before host.other_config (CA-75625);
|
|
// We'll check it again after the cache update operation is complete, in Connection_XenObjectsUpdated
|
|
hostsInInvalidState.Add(host);
|
|
}
|
|
UpdateToolbars();
|
|
break;
|
|
case "edition":
|
|
case "license_server":
|
|
case "license_params":
|
|
case "other_config":
|
|
// other_config may contain HideFromXenCenter
|
|
UpdateToolbars();
|
|
break;
|
|
|
|
case "name_label":
|
|
//check whether it's a standalone host
|
|
if(Helpers.GetPool(host.Connection) == null)
|
|
host.Connection.FriendlyName = Helpers.GetName(host);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void VM_PropertyChanged(object obj, PropertyChangedEventArgs e)
|
|
{
|
|
VM vm = (VM)obj;
|
|
switch (e.PropertyName)
|
|
{
|
|
case "allowed_operations":
|
|
case "is_a_template":
|
|
case "resident_on":
|
|
UpdateToolbars();
|
|
break;
|
|
|
|
case "power_state":
|
|
UpdateToolbars();
|
|
// Make all vms have the correct start times
|
|
UpdateBodgedTime(vm, e.PropertyName);
|
|
break;
|
|
|
|
case "other_config":
|
|
// other_config may contain HideFromXenCenter
|
|
UpdateToolbars();
|
|
|
|
// Make all vms have the correct start times
|
|
UpdateBodgedTime(vm, e.PropertyName);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void o_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
switch (e.PropertyName)
|
|
{
|
|
case "allowed_operations":
|
|
case "power_state":
|
|
case "is_a_template":
|
|
case "enabled":
|
|
UpdateToolbars();
|
|
break;
|
|
|
|
case "other_config":
|
|
// other_config may contain HideFromXenCenter
|
|
UpdateToolbars();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Update bodged startup time if the powerstate goes to running (vm started from halted), otherconfig last shutdown changed (vm rebooted) or start time changed (occurs a few seconds after start)
|
|
private void UpdateBodgedTime(VM vm, string p)
|
|
{
|
|
if (vm == null)
|
|
return;
|
|
if (p == "power_state")
|
|
{
|
|
vm.BodgeStartupTime = DateTime.UtcNow; // always newer than current bodge startup time
|
|
}
|
|
else if (p == "other_config" && vm.other_config.ContainsKey("last_shutdown_time"))
|
|
{
|
|
DateTime newTime = vm.LastShutdownTime;
|
|
if (newTime != DateTime.MinValue && newTime.Ticks > vm.BodgeStartupTime.Ticks)
|
|
vm.BodgeStartupTime = newTime; // only update if is newer than current bodge startup time
|
|
}
|
|
}
|
|
|
|
void Connection_ConnectionResult(object sender, Network.ConnectionResultEventArgs e)
|
|
{
|
|
RequestRefreshTreeView();
|
|
Program.Invoke(this, (EventHandler<ConnectionResultEventArgs>)Connection_ConnectionResult_, sender, e);
|
|
}
|
|
|
|
private void Connection_ConnectionResult_(object sender, Network.ConnectionResultEventArgs e)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
try
|
|
{
|
|
UpdateToolbars();
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Error(exn, exn);
|
|
// Can do nothing more about this.
|
|
}
|
|
}
|
|
|
|
void Connection_ConnectionClosed(object sender, EventArgs e)
|
|
{
|
|
RequestRefreshTreeView();
|
|
Program.Invoke(this, (EventHandler<Network.ConnectionResultEventArgs>)Connection_ConnectionClosed_, sender, e);
|
|
gc();
|
|
}
|
|
|
|
private void Connection_ConnectionClosed_(object sender, EventArgs e)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
try
|
|
{
|
|
UpdateToolbars();
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Error(exn, exn);
|
|
// Nothing more we can do with this.
|
|
}
|
|
}
|
|
|
|
// called whenever our connection with the Xen server fails (i.e., after we've successfully logged in)
|
|
void Connection_ConnectionLost(object sender, EventArgs e)
|
|
{
|
|
if (Program.Exiting)
|
|
return;
|
|
Program.Invoke(this, (EventHandler)Connection_ConnectionLost_, sender, e);
|
|
RequestRefreshTreeView();
|
|
gc();
|
|
}
|
|
|
|
private void Connection_ConnectionLost_(object sender, EventArgs e)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
try
|
|
{
|
|
IXenConnection connection = (IXenConnection)sender;
|
|
CloseActiveWizards(connection);
|
|
|
|
UpdateToolbars();
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Error(exn, exn);
|
|
// Can do nothing about this.
|
|
}
|
|
}
|
|
|
|
private static void gc()
|
|
{
|
|
log_gc("Before");
|
|
GC.Collect();
|
|
log_gc("After");
|
|
}
|
|
|
|
private static void log_gc(string when)
|
|
{
|
|
log.DebugFormat("{0} GC: approx {1} bytes in use", when, GC.GetTotalMemory(false));
|
|
for (int i = 0; i <= GC.MaxGeneration; i++)
|
|
{
|
|
log.DebugFormat("Number of times GC has occurred for generation {0} objects: {1}", i, GC.CollectionCount(i));
|
|
}
|
|
log.Debug("GDI objects in use: " + Win32.GetGuiResourcesGDICount(Process.GetCurrentProcess().Handle));
|
|
log.Debug("USER objects in use: " + Win32.GetGuiResourcesUserCount(Process.GetCurrentProcess().Handle));
|
|
}
|
|
|
|
void connection_ConnectionReconnecting(object sender, EventArgs e)
|
|
{
|
|
if (Program.Exiting)
|
|
return;
|
|
RequestRefreshTreeView();
|
|
gc();
|
|
}
|
|
|
|
private List<Host> hostsInInvalidState = new List<Host>();
|
|
|
|
// called whenever Xen objects on the server change state
|
|
void Connection_XenObjectsUpdated(object sender, EventArgs e)
|
|
{
|
|
if (Program.Exiting)
|
|
return;
|
|
|
|
IXenConnection connection = (IXenConnection) sender;
|
|
|
|
if (hostsInInvalidState.Count > 0)
|
|
{
|
|
foreach (var host in hostsInInvalidState.Where(host => host.Connection == connection))
|
|
CheckMaintenanceMode(host);
|
|
hostsInInvalidState.RemoveAll(host => host.Connection == connection);
|
|
}
|
|
|
|
RequestRefreshTreeView();
|
|
}
|
|
|
|
private int ignoreUpdateToolbars = 0;
|
|
private bool calledUpdateToolbars = false;
|
|
|
|
/// <summary>
|
|
/// Requests a refresh of the main tree view. The refresh will be managed such that we are not overloaded using an UpdateManager.
|
|
/// </summary>
|
|
public void RequestRefreshTreeView()
|
|
{
|
|
Program.Invoke(this, navigationPane.RequestRefreshTreeView);
|
|
}
|
|
|
|
private void UpdateHeaderAndTabPages()
|
|
{
|
|
Program.Invoke(this, () =>
|
|
{
|
|
// This is required to update search results when things change.
|
|
if (TheTabControl.SelectedTab == TabPageGeneral)
|
|
GeneralPage.BuildList();
|
|
else if (TheTabControl.SelectedTab == TabPageSearch)
|
|
SearchPage.BuildList();
|
|
|
|
UpdateHeader();
|
|
});
|
|
}
|
|
|
|
void exitToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
this.Close();
|
|
}
|
|
|
|
private bool _menuShortcuts = true;
|
|
public bool MenuShortcuts
|
|
{
|
|
set
|
|
{
|
|
if (value != _menuShortcuts)
|
|
{
|
|
//if the VNC Console is active (the user is typing into it etc) all of the shortcuts for XenCenter are disabled
|
|
//IMPORTANT! add any shortcuts you want to pass to the VNC console into this if, else statement
|
|
_menuShortcuts = value;
|
|
|
|
// update the selection so menu items can enable/disable keyboard shortcuts as appropriate.
|
|
SelectionManager.RefreshSelection();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Must be called on the event thread.
|
|
/// </summary>
|
|
public void UpdateToolbars()
|
|
{
|
|
Program.AssertOnEventThread();
|
|
|
|
if (ignoreUpdateToolbars > 0)
|
|
{
|
|
calledUpdateToolbars = true;
|
|
return;
|
|
}
|
|
|
|
ToolStrip.SuspendLayout();
|
|
|
|
UpdateToolbarsCore();
|
|
MainMenuBar_MenuActivate(null, null);
|
|
|
|
ToolStrip.ResumeLayout();
|
|
}
|
|
|
|
private static int TOOLBAR_HEIGHT = 31;
|
|
/// <summary>
|
|
/// Updates the toolbar buttons. Also updates which tabs are visible.
|
|
/// </summary>
|
|
public void UpdateToolbarsCore()
|
|
{
|
|
// refresh the selection-manager
|
|
SelectionManager.RefreshSelection();
|
|
|
|
ToolStrip.Height = ToolbarsEnabled ? TOOLBAR_HEIGHT : 0;
|
|
ToolStrip.Enabled = ToolbarsEnabled;
|
|
ShowToolbarMenuItem.Checked = toolbarToolStripMenuItem.Checked = ToolbarsEnabled;
|
|
|
|
powerOnHostToolStripButton.Available = powerOnHostToolStripButton.Enabled;
|
|
startVMToolStripButton.Available = startVMToolStripButton.Enabled;
|
|
shutDownToolStripButton.Available = shutDownToolStripButton.Enabled || (!startVMToolStripButton.Available && !powerOnHostToolStripButton.Available);
|
|
|
|
resumeToolStripButton.Available = resumeToolStripButton.Enabled;
|
|
SuspendToolbarButton.Available = SuspendToolbarButton.Enabled || !resumeToolStripButton.Available;
|
|
|
|
ForceRebootToolbarButton.Available = ((ForceVMRebootCommand)ForceRebootToolbarButton.Command).ShowOnMainToolBar;
|
|
ForceShutdownToolbarButton.Available = ((ForceVMShutDownCommand)ForceShutdownToolbarButton.Command).ShowOnMainToolBar;
|
|
|
|
IXenConnection selectionConnection = SelectionManager.Selection.GetConnectionOfFirstItem();
|
|
Pool selectionPool = selectionConnection == null ? null : Helpers.GetPool(selectionConnection);
|
|
Host selectionMaster = null == selectionPool ? null : selectionPool.Connection.Resolve(selectionPool.master);
|
|
|
|
// 'Home' tab is only visible if the 'Overview' tree node is selected, or if the tree is
|
|
// empty (i.e. at startup).
|
|
bool show_home = SelectionManager.Selection.Count == 1 && SelectionManager.Selection[0].Value == null;
|
|
// Only show the HA tab if the host's license has the HA flag set
|
|
bool has_ha_license_flag = selectionMaster != null && !selectionMaster.RestrictHAOrlando;
|
|
bool george_or_greater = Helpers.GeorgeOrGreater(selectionConnection);
|
|
bool mr_or_greater = Helpers.MidnightRideOrGreater(selectionConnection);
|
|
// The upsell pages use the first selected XenObject: but they're only shown if there is only one selected object (see calls to ShowTab() below).
|
|
bool dmc_upsell = Helpers.FeatureForbidden(SelectionManager.Selection.FirstAsXenObject, Host.RestrictDMC);
|
|
bool ha_upsell = Helpers.FeatureForbidden(SelectionManager.Selection.FirstAsXenObject, Host.RestrictHAFloodgate);
|
|
bool wlb_upsell = Helpers.FeatureForbidden(SelectionManager.Selection.FirstAsXenObject, Host.RestrictWLB);
|
|
bool is_connected = selectionConnection != null && selectionConnection.IsConnected;
|
|
|
|
bool multi = SelectionManager.Selection.Count > 1;
|
|
|
|
bool isPoolSelected = SelectionManager.Selection.FirstIsPool;
|
|
bool isVMSelected = SelectionManager.Selection.FirstIsVM;
|
|
bool isHostSelected = SelectionManager.Selection.FirstIsHost;
|
|
bool isSRSelected = SelectionManager.Selection.FirstIsSR;
|
|
bool isRealVMSelected = SelectionManager.Selection.FirstIsRealVM;
|
|
bool isTemplateSelected = SelectionManager.Selection.FirstIsTemplate;
|
|
bool isHostLive = SelectionManager.Selection.FirstIsLiveHost;
|
|
bool isStorageLinkSelected = SelectionManager.Selection.FirstIsStorageLink;
|
|
bool isStorageLinkSRSelected = SelectionManager.Selection.First is StorageLinkRepository && ((StorageLinkRepository)SelectionManager.Selection.First).SR(ConnectionsManager.XenConnectionsCopy) != null;
|
|
|
|
bool selectedTemplateHasProvisionXML = SelectionManager.Selection.FirstIsTemplate && ((VM)SelectionManager.Selection[0].XenObject).HasProvisionXML;
|
|
|
|
NewTabCount = 0;
|
|
ShowTab(TabPageHome, !SearchMode && show_home);
|
|
ShowTab(TabPageGeneral, !multi && !SearchMode && (isVMSelected || (isHostSelected && (isHostLive || !is_connected)) || isPoolSelected || isSRSelected || isStorageLinkSelected));
|
|
ShowTab(dmc_upsell ? TabPageBallooningUpsell : TabPageBallooning, !multi && !SearchMode && mr_or_greater && (isVMSelected || (isHostSelected && isHostLive) || isPoolSelected));
|
|
ShowTab(TabPageStorage, !multi && !SearchMode && (isRealVMSelected || (isTemplateSelected && !selectedTemplateHasProvisionXML)));
|
|
ShowTab(TabPageSR, !multi && !SearchMode && (isSRSelected || isStorageLinkSRSelected));
|
|
ShowTab(TabPagePhysicalStorage, !multi && !SearchMode && ((isHostSelected && isHostLive) || isPoolSelected));
|
|
ShowTab(TabPageNetwork, !multi && !SearchMode && (isVMSelected || (isHostSelected && isHostLive) || isPoolSelected));
|
|
ShowTab(TabPageNICs, !multi && !SearchMode && ((isHostSelected && isHostLive)));
|
|
|
|
bool isPoolOrLiveStandaloneHost = isPoolSelected || (isHostSelected && isHostLive && selectionPool == null);
|
|
bool showGpu = SelectionManager.Selection.All(s => Helpers.ClearwaterOrGreater(s.Connection)) &&
|
|
!Helpers.FeatureForbidden(SelectionManager.Selection.FirstAsXenObject, Host.RestrictVgpu) &&
|
|
Helpers.VgpuCapability(selectionConnection);
|
|
|
|
ShowTab(TabPageGPU, !multi && !SearchMode && ((isHostSelected && isHostLive) || isPoolOrLiveStandaloneHost) && showGpu);
|
|
|
|
pluginManager.SetSelectedXenObject(SelectionManager.Selection.FirstAsXenObject);
|
|
|
|
bool shownConsoleReplacement = false;
|
|
foreach (TabPageFeature f in pluginManager.GetAllFeatures<TabPageFeature>(f => f.IsConsoleReplacement && !f.IsError && !multi && f.ShowTab))
|
|
{
|
|
ShowTab(f.TabPage, true);
|
|
shownConsoleReplacement = true;
|
|
}
|
|
|
|
ShowTab(TabPageConsole, !shownConsoleReplacement && !multi && !SearchMode && (isRealVMSelected || (isHostSelected && isHostLive)));
|
|
ShowTab(TabPagePeformance, !multi && !SearchMode && (isRealVMSelected || (isHostSelected && isHostLive)));
|
|
ShowTab(ha_upsell ? TabPageHAUpsell : TabPageHA, !multi && !SearchMode && isPoolSelected && has_ha_license_flag);
|
|
ShowTab(TabPageSnapshots, !multi && !SearchMode && george_or_greater && isRealVMSelected);
|
|
|
|
//Any Clearwater XenServer, or WLB is not licensed on XenServer, the WLB tab and any WLB menu items disappear completely.
|
|
if(!(SelectionManager.Selection.All(s => Helpers.IsClearwater(s.Connection)) || wlb_upsell ))
|
|
ShowTab(TabPageWLB, !multi && !SearchMode && isPoolSelected && george_or_greater);
|
|
|
|
ShowTab(TabPageAD, !multi && !SearchMode && (isPoolSelected || isHostSelected && isHostLive) && george_or_greater);
|
|
|
|
foreach (TabPageFeature f in pluginManager.GetAllFeatures<TabPageFeature>(f => !f.IsConsoleReplacement && !multi && f.ShowTab))
|
|
ShowTab(f.TabPage, true);
|
|
|
|
ShowTab(TabPageSearch, true);
|
|
|
|
// N.B. Change NewTabs definition if you add more tabs here.
|
|
|
|
// Save and restore focus on treeView, since selecting tabs in ChangeToNewTabs() has the
|
|
// unavoidable side-effect of giving them focus - this is irritating if trying to navigate
|
|
// the tree using the keyboard.
|
|
|
|
navigationPane.SaveAndRestoreTreeViewFocus(ChangeToNewTabs);
|
|
}
|
|
|
|
private readonly TabPage[] NewTabs = new TabPage[512];
|
|
int NewTabCount;
|
|
private void ShowTab(TabPage page, bool visible)
|
|
{
|
|
if (visible)
|
|
{
|
|
NewTabs[NewTabCount] = page;
|
|
NewTabCount++;
|
|
}
|
|
}
|
|
|
|
private void ChangeToNewTabs()
|
|
{
|
|
TabPage new_selected_page = NewSelectedPage();
|
|
TheTabControl.SuspendLayout();
|
|
IgnoreTabChanges = true;
|
|
try
|
|
{
|
|
TabControl.TabPageCollection p = TheTabControl.TabPages;
|
|
int i = 0; // Index into NewTabs
|
|
int m = 0; // Index into p
|
|
|
|
while (i < NewTabCount)
|
|
{
|
|
if (m == p.Count)
|
|
{
|
|
p.Add(NewTabs[i]);
|
|
if (new_selected_page == NewTabs[i])
|
|
TheTabControl.SelectedTab = new_selected_page;
|
|
m++;
|
|
i++;
|
|
}
|
|
else if (p[m] == NewTabs[i])
|
|
{
|
|
if (new_selected_page == NewTabs[i])
|
|
TheTabControl.SelectedTab = new_selected_page;
|
|
m++;
|
|
i++;
|
|
}
|
|
else if (NewTabs.Contains(p[m]))
|
|
{
|
|
p.Insert(m, NewTabs[i]);
|
|
if (new_selected_page == NewTabs[i])
|
|
TheTabControl.SelectedTab = new_selected_page;
|
|
m++;
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
if (TheTabControl.SelectedTab == p[m] && new_selected_page == NewTabs[i])
|
|
{
|
|
// This clause is deliberately targeted at the case when you go from
|
|
// Overview:Home to Host:Overview.
|
|
p.Insert(m, NewTabs[i]);
|
|
TheTabControl.SelectedTab = new_selected_page;
|
|
m++;
|
|
i++;
|
|
}
|
|
p.Remove(p[m]);
|
|
}
|
|
}
|
|
|
|
// Remove any tabs that are left at the end of the list.
|
|
while (m < p.Count)
|
|
{
|
|
TabPage removed = p[p.Count - 1];
|
|
p.Remove(removed);
|
|
int index = NewTabsIndexOf(removed);
|
|
if (index != -1)
|
|
{
|
|
// If this is a tab that we want, then we've got it in the list twice -- one
|
|
// reference was here when we entered this function, and is the one that we're
|
|
// pointing at now, and the other reference we've inserted through the loop above.
|
|
// This is bad -- p.Remove(removed) above has now invalidated removed.Parent
|
|
// (removed is still in the list through the other reference). Fix this up by
|
|
// removing the other reference too and starting over.
|
|
// We can't do the Remove call in the loop above, because this has a poor visual
|
|
// effect.
|
|
p.Remove(removed);
|
|
p.Insert(index, removed);
|
|
if (new_selected_page == removed)
|
|
TheTabControl.SelectedTab = removed;
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
IgnoreTabChanges = false;
|
|
TheTabControl.ResumeLayout();
|
|
|
|
SetLastSelectedPage(SelectionManager.Selection.First, TheTabControl.SelectedTab);
|
|
}
|
|
}
|
|
|
|
private TabPage NewSelectedPage()
|
|
{
|
|
Object o = SelectionManager.Selection.First;
|
|
IXenObject s = o as IXenObject;
|
|
|
|
TabPage last_selected_page = GetLastSelectedPage(o);
|
|
return last_selected_page != null && NewTabs.Contains(last_selected_page)
|
|
? last_selected_page
|
|
: NewTabs[0];
|
|
}
|
|
|
|
private void SetLastSelectedPage(object o, TabPage p)
|
|
{
|
|
if (SearchMode)
|
|
return;
|
|
|
|
if (o == null)
|
|
{
|
|
selectedOverviewTab = p;
|
|
}
|
|
else
|
|
{
|
|
selectedTabs[o] = p;
|
|
}
|
|
}
|
|
|
|
private TabPage GetLastSelectedPage(object o)
|
|
{
|
|
return
|
|
o == null ? selectedOverviewTab :
|
|
selectedTabs.ContainsKey(o) ? selectedTabs[o] :
|
|
null;
|
|
}
|
|
|
|
private int NewTabsIndexOf(TabPage tp)
|
|
{
|
|
for (int i = 0; i < NewTabCount; i++)
|
|
{
|
|
if (NewTabs[i] == tp)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private void pluginManager_PluginsChanged()
|
|
{
|
|
foreach (ToolStripMenuItem menu in MainMenuBar.Items)
|
|
{
|
|
//clear existing plugin items
|
|
for (int i = menu.DropDownItems.Count - 1; i >= 0; i--)
|
|
{
|
|
CommandToolStripMenuItem commandMenuItem = menu.DropDownItems[i] as CommandToolStripMenuItem;
|
|
|
|
if (commandMenuItem != null && (commandMenuItem.Command is MenuItemFeatureCommand
|
|
|| commandMenuItem.Command is ParentMenuItemFeatureCommand))
|
|
{
|
|
menu.DropDownItems.RemoveAt(i);
|
|
|
|
if (menu.DropDownItems.Count > 0 && menu.DropDownItems[i] is ToolStripSeparator)
|
|
menu.DropDownItems.RemoveAt(i);
|
|
}
|
|
}
|
|
|
|
// get insert index using the placeholder
|
|
int insertIndex = pluginMenuItemStartIndexes[menu];
|
|
|
|
bool itemAdded = false;
|
|
|
|
// add plugin items for this menu at insertIndex
|
|
foreach (PluginDescriptor plugin in pluginManager.Plugins)
|
|
{
|
|
if (!plugin.Enabled)
|
|
continue;
|
|
|
|
foreach (Feature feature in plugin.Features)
|
|
{
|
|
var menuItemFeature = feature as MenuItemFeature;
|
|
|
|
if (menuItemFeature != null && menuItemFeature.ParentFeature == null && (int)menuItemFeature.Menu == MainMenuBar.Items.IndexOf(menu))
|
|
{
|
|
Command cmd = menuItemFeature.GetCommand(this, SelectionManager.Selection);
|
|
|
|
menu.DropDownItems.Insert(insertIndex, new CommandToolStripMenuItem(cmd));
|
|
insertIndex++;
|
|
itemAdded = true;
|
|
}
|
|
|
|
var parentMenuItemFeature = feature as ParentMenuItemFeature;
|
|
|
|
if (parentMenuItemFeature != null && (int)parentMenuItemFeature.Menu == MainMenuBar.Items.IndexOf(menu))
|
|
{
|
|
Command cmd = parentMenuItemFeature.GetCommand(this, SelectionManager.Selection);
|
|
CommandToolStripMenuItem parentMenuItem = new CommandToolStripMenuItem(cmd);
|
|
|
|
menu.DropDownItems.Insert(insertIndex, parentMenuItem);
|
|
insertIndex++;
|
|
itemAdded = true;
|
|
|
|
foreach (MenuItemFeature childFeature in parentMenuItemFeature.Features)
|
|
{
|
|
Command childCommand = childFeature.GetCommand(this, SelectionManager.Selection);
|
|
parentMenuItem.DropDownItems.Add(new CommandToolStripMenuItem(childCommand));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (itemAdded && insertIndex != menu.DropDownItems.Count)
|
|
menu.DropDownItems.Insert(insertIndex, new ToolStripSeparator());
|
|
}
|
|
|
|
windowToolStripMenuItem.Visible = windowToolStripMenuItem.DropDownItems.Count > 0;
|
|
}
|
|
|
|
private void MainMenuBar_MenuActivate(object sender, EventArgs e)
|
|
{
|
|
Host hostAncestor = SelectionManager.Selection.Count == 1 ? SelectionManager.Selection[0].HostAncestor : null;
|
|
IXenConnection connection = SelectionManager.Selection.GetConnectionOfFirstItem();
|
|
bool vm = SelectionManager.Selection.FirstIsRealVM && !((VM)SelectionManager.Selection.First).Locked;
|
|
|
|
Host best_host = hostAncestor ?? (connection == null ? null : Helpers.GetMaster(connection));
|
|
bool george_or_greater = best_host != null && Helpers.GeorgeOrGreater(best_host);
|
|
|
|
exportSettingsToolStripMenuItem.Enabled = ConnectionsManager.XenConnectionsCopy.Count > 0;
|
|
|
|
this.MenuShortcuts = true;
|
|
|
|
startOnHostToolStripMenuItem.Available = startOnHostToolStripMenuItem.Enabled;
|
|
resumeOnToolStripMenuItem.Available = resumeOnToolStripMenuItem.Enabled;
|
|
relocateToolStripMenuItem.Available = relocateToolStripMenuItem.Enabled;
|
|
storageLinkToolStripMenuItem.Available = storageLinkToolStripMenuItem.Enabled;
|
|
sendCtrlAltDelToolStripMenuItem.Enabled = (TheTabControl.SelectedTab == TabPageConsole) && vm && ((VM)SelectionManager.Selection.First).power_state == vm_power_state.Running;
|
|
|
|
templatesToolStripMenuItem1.Checked = Properties.Settings.Default.DefaultTemplatesVisible;
|
|
customTemplatesToolStripMenuItem.Checked = Properties.Settings.Default.UserTemplatesVisible;
|
|
localStorageToolStripMenuItem.Checked = Properties.Settings.Default.LocalSRsVisible;
|
|
ShowHiddenObjectsToolStripMenuItem.Checked = Properties.Settings.Default.ShowHiddenVMs;
|
|
connectDisconnectToolStripMenuItem.Enabled = ConnectionsManager.XenConnectionsCopy.Count > 0;
|
|
}
|
|
|
|
private void xenSourceOnTheWebToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
Program.OpenURL(InvisibleMessages.HOMEPAGE);
|
|
}
|
|
|
|
private void xenCenterPluginsOnTheWebToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
Program.OpenURL(InvisibleMessages.PLUGINS_URL);
|
|
}
|
|
|
|
private void aboutXenSourceAdminToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
ShowForm(typeof(AboutDialog));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply license, if HostAncestorOfSelectedNode is null, show host picker, if filepath == "" show filepicker
|
|
/// </summary>
|
|
public void LaunchLicensePicker(string filepath)
|
|
{
|
|
HelpersGUI.BringFormToFront(this);
|
|
OpenFileDialog dialog = null;
|
|
DialogResult result = DialogResult.Cancel;
|
|
if (filepath == "")
|
|
{
|
|
if (!Program.RunInAutomatedTestMode)
|
|
{
|
|
dialog = new OpenFileDialog();
|
|
dialog.Multiselect = false;
|
|
dialog.Title = Messages.INSTALL_LICENSE_KEY;
|
|
dialog.CheckFileExists = true;
|
|
dialog.CheckPathExists = true;
|
|
dialog.Filter = string.Format("{0} (*.xslic)|*.xslic|{1} (*.*)|*.*", Messages.XS_LICENSE_FILES, Messages.ALL_FILES);
|
|
dialog.ShowHelp = true;
|
|
dialog.HelpRequest += new EventHandler(dialog_HelpRequest);
|
|
result = dialog.ShowDialog(this);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = DialogResult.OK;
|
|
}
|
|
|
|
if (result == DialogResult.OK || Program.RunInAutomatedTestMode)
|
|
{
|
|
filepath = Program.RunInAutomatedTestMode ? "" : filepath == "" ? dialog.FileName : filepath;
|
|
|
|
Host hostAncestor = SelectionManager.Selection.Count == 1 ? SelectionManager.Selection[0].HostAncestor : null;
|
|
|
|
if (SelectionManager.Selection.Count == 1 && hostAncestor == null)
|
|
{
|
|
SelectHostDialog hostdialog = new SelectHostDialog();
|
|
hostdialog.TheHost = null;
|
|
hostdialog.Owner = this;
|
|
hostdialog.ShowDialog(this);
|
|
if (string.IsNullOrEmpty(filepath) || hostdialog.DialogResult != DialogResult.OK)
|
|
{
|
|
return;
|
|
}
|
|
hostAncestor = hostdialog.TheHost;
|
|
}
|
|
|
|
DoLicenseAction(hostAncestor, filepath);
|
|
|
|
}
|
|
}
|
|
|
|
private void DoLicenseAction(Host host, string filePath)
|
|
{
|
|
ApplyLicenseAction action = new ApplyLicenseAction(host.Connection, host, filePath);
|
|
ActionProgressDialog actionProgress = new ActionProgressDialog(action, ProgressBarStyle.Marquee);
|
|
|
|
actionProgress.Text = Messages.INSTALL_LICENSE_KEY;
|
|
actionProgress.ShowDialog(this);
|
|
}
|
|
|
|
private void dialog_HelpRequest(object sender, EventArgs e)
|
|
{
|
|
Help.HelpManager.Launch("LicenseKeyDialog");
|
|
}
|
|
|
|
/// <param name="e">
|
|
/// If null, then we deduce the method was called by TreeView_AfterSelect
|
|
/// and don't focus the VNC console. i.e. we only focus the VNC console if the user
|
|
/// explicitly clicked on the console tab rather than arriving there by navigating
|
|
/// in treeView.
|
|
/// </param>
|
|
private void TheTabControl_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
if (IgnoreTabChanges)
|
|
return;
|
|
|
|
TabPage t = TheTabControl.SelectedTab;
|
|
|
|
if (!SearchMode)
|
|
History.NewHistoryItem(new XenModelObjectHistoryItem(SelectionManager.Selection.FirstAsXenObject, t));
|
|
|
|
if (t != TabPageBallooning)
|
|
BallooningPage.IsHidden();
|
|
|
|
if (t == TabPageConsole)
|
|
{
|
|
if (SelectionManager.Selection.FirstIsRealVM)
|
|
{
|
|
ConsolePanel.setCurrentSource((VM)SelectionManager.Selection.First);
|
|
UnpauseVNC(e != null && sender == TheTabControl);
|
|
}
|
|
else if (SelectionManager.Selection.FirstIsHost)
|
|
{
|
|
ConsolePanel.setCurrentSource((Host)SelectionManager.Selection.First);
|
|
UnpauseVNC(e != null && sender == TheTabControl);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ConsolePanel.PauseAllViews();
|
|
|
|
// Start timer for closing the VNC connection after an interval (20 seconds)
|
|
// when the console tab is not selected
|
|
ConsolePanel.StartCloseVNCTimer(ConsolePanel.activeVNCView);
|
|
|
|
if (t == TabPageGeneral)
|
|
{
|
|
GeneralPage.XenObject = SelectionManager.Selection.FirstAsXenObject;
|
|
}
|
|
else if (t == TabPageBallooning)
|
|
{
|
|
BallooningPage.XenObject = SelectionManager.Selection.FirstAsXenObject;
|
|
}
|
|
else if (t == TabPageSR)
|
|
{
|
|
StorageLinkRepository slr = SelectionManager.Selection.First as StorageLinkRepository;
|
|
SrStoragePage.SR = slr == null ? SelectionManager.Selection.First as SR : slr.SR(ConnectionsManager.XenConnectionsCopy);
|
|
}
|
|
else if (t == TabPageNetwork)
|
|
{
|
|
NetworkPage.XenObject = SelectionManager.Selection.FirstAsXenObject;
|
|
}
|
|
else if (t == TabPageNICs)
|
|
{
|
|
NICPage.Host = SelectionManager.Selection.First as Host;
|
|
}
|
|
else if (t == TabPageStorage)
|
|
{
|
|
VMStoragePage.VM = SelectionManager.Selection.First as VM;
|
|
}
|
|
else if (t == TabPagePeformance)
|
|
{
|
|
PerformancePage.XenObject = SelectionManager.Selection.FirstAsXenObject;
|
|
}
|
|
else if (t == TabPageSearch && !SearchMode)
|
|
{
|
|
var rootNode = SelectionManager.Selection.RootNode;
|
|
var rootNodeGrouping = rootNode == null ? null : rootNode.Tag as GroupingTag;
|
|
var search = rootNode == null ? null : rootNode.Tag as Search;
|
|
|
|
if (search != null)
|
|
{
|
|
SearchPage.Search = search;
|
|
}
|
|
else if (rootNodeGrouping != null)
|
|
{
|
|
var objectsView = rootNodeGrouping.Grouping as OrganizationViewObjects;
|
|
var vappsView = rootNodeGrouping.Grouping as OrganizationViewVapps;
|
|
var foldersView = rootNodeGrouping.Grouping as OrganizationViewFolders;
|
|
|
|
if (vappsView != null)
|
|
{
|
|
SearchPage.Search = Search.SearchForVappGroup(rootNodeGrouping.Grouping,
|
|
rootNodeGrouping.Parent, rootNodeGrouping.Group);
|
|
}
|
|
else if (objectsView != null)
|
|
{
|
|
GroupingTag gt = SelectionManager.Selection.First as GroupingTag
|
|
?? SelectionManager.Selection.GroupAncestor;
|
|
|
|
SearchPage.Search = Search.SearchForNonVappGroup(gt.Grouping, gt.Parent, gt.Group);
|
|
}
|
|
else if (foldersView != null)
|
|
{
|
|
SearchPage.Search = Search.SearchForFolderGroup(rootNodeGrouping.Grouping,
|
|
rootNodeGrouping.Parent, rootNodeGrouping.Group);
|
|
}
|
|
else
|
|
{
|
|
SearchPage.Search = Search.SearchForNonVappGroup(rootNodeGrouping.Grouping,
|
|
rootNodeGrouping.Parent, rootNodeGrouping.Group);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SearchPage.XenObject = null;
|
|
}
|
|
}
|
|
else if (t == TabPageHA)
|
|
{
|
|
HAPage.XenObject = SelectionManager.Selection.FirstAsXenObject;
|
|
}
|
|
else if (t == TabPageWLB)
|
|
{
|
|
WlbPage.Pool = SelectionManager.Selection.First as Pool;
|
|
}
|
|
else if (t == TabPageSnapshots)
|
|
{
|
|
snapshotPage.VM = SelectionManager.Selection.First as VM;
|
|
}
|
|
else if (t == TabPagePhysicalStorage)
|
|
{
|
|
PhysicalStoragePage.SetSelectionBroadcaster(SelectionManager, this);
|
|
PhysicalStoragePage.Host = SelectionManager.Selection.First as Host;
|
|
PhysicalStoragePage.Connection = SelectionManager.Selection.GetConnectionOfFirstItem();
|
|
}
|
|
else if (t == TabPageAD)
|
|
{
|
|
AdPage.XenObject = SelectionManager.Selection.FirstAsXenObject;
|
|
}
|
|
else if (t == TabPageGPU)
|
|
{
|
|
GpuPage.XenObject = SelectionManager.Selection.FirstAsXenObject;
|
|
}
|
|
}
|
|
|
|
if (t == TabPagePeformance)
|
|
PerformancePage.ResumeGraph();
|
|
else
|
|
PerformancePage.PauseGraph();
|
|
|
|
if (t == TabPageSearch)
|
|
SearchPage.PanelShown();
|
|
else
|
|
SearchPage.PanelHidden();
|
|
|
|
if (t != null)
|
|
SetLastSelectedPage(SelectionManager.Selection.First, t);
|
|
}
|
|
|
|
private void UnpauseVNC(bool focus)
|
|
{
|
|
ConsolePanel.UnpauseActiveView();
|
|
if (focus)
|
|
{
|
|
ConsolePanel.FocusActiveView();
|
|
ConsolePanel.SwitchIfRequired();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The tabs that may be visible in the main GUI window. Used in SwitchToTab().
|
|
/// </summary>
|
|
public enum Tab
|
|
{
|
|
Overview, Home, Settings, Storage, Network, Console, Performance, NICs, SR
|
|
}
|
|
|
|
public void SwitchToTab(Tab tab)
|
|
{
|
|
switch (tab)
|
|
{
|
|
case Tab.Overview:
|
|
TheTabControl.SelectedTab = TabPageSearch;
|
|
break;
|
|
case Tab.Home:
|
|
TheTabControl.SelectedTab = TabPageHome;
|
|
break;
|
|
case Tab.Settings:
|
|
TheTabControl.SelectedTab = TabPageGeneral;
|
|
break;
|
|
case Tab.Storage:
|
|
TheTabControl.SelectedTab = TabPageStorage;
|
|
break;
|
|
case Tab.Network:
|
|
TheTabControl.SelectedTab = TabPageNetwork;
|
|
break;
|
|
case Tab.Console:
|
|
TheTabControl.SelectedTab = TabPageConsole;
|
|
break;
|
|
case Tab.Performance:
|
|
TheTabControl.SelectedTab = TabPagePeformance;
|
|
break;
|
|
case Tab.NICs:
|
|
TheTabControl.SelectedTab = TabPageNICs;
|
|
break;
|
|
case Tab.SR:
|
|
TheTabControl.SelectedTab = TabPageSR;
|
|
break;
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
|
|
private void templatesToolStripMenuItem1_Click(object sender, EventArgs e)
|
|
{
|
|
templatesToolStripMenuItem1.Checked = !templatesToolStripMenuItem1.Checked;
|
|
Properties.Settings.Default.DefaultTemplatesVisible = templatesToolStripMenuItem1.Checked;
|
|
ViewSettingsChanged();
|
|
}
|
|
|
|
private void customTemplatesToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
customTemplatesToolStripMenuItem.Checked = !customTemplatesToolStripMenuItem.Checked;
|
|
Properties.Settings.Default.UserTemplatesVisible = customTemplatesToolStripMenuItem.Checked;
|
|
ViewSettingsChanged();
|
|
}
|
|
|
|
private void localStorageToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
localStorageToolStripMenuItem.Checked = !localStorageToolStripMenuItem.Checked;
|
|
Properties.Settings.Default.LocalSRsVisible = localStorageToolStripMenuItem.Checked;
|
|
ViewSettingsChanged();
|
|
}
|
|
|
|
private void ShowHiddenObjectsToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
ShowHiddenObjectsToolStripMenuItem.Checked = !ShowHiddenObjectsToolStripMenuItem.Checked;
|
|
Properties.Settings.Default.ShowHiddenVMs = ShowHiddenObjectsToolStripMenuItem.Checked;
|
|
ViewSettingsChanged();
|
|
}
|
|
|
|
private void ViewSettingsChanged()
|
|
{
|
|
Settings.TrySaveSettings();
|
|
navigationPane.UpdateSearch();
|
|
RequestRefreshTreeView();
|
|
}
|
|
|
|
private void EditSelectedNodeInTreeView()
|
|
{
|
|
navigationPane.EditSelectedNode();
|
|
}
|
|
|
|
protected override void OnClosing(CancelEventArgs e)
|
|
{
|
|
bool currentTasks = false;
|
|
foreach (ActionBase a in ConnectionsManager.History)
|
|
{
|
|
if (a is MeddlingAction || a.IsCompleted)
|
|
continue;
|
|
|
|
currentTasks = true;
|
|
break;
|
|
}
|
|
|
|
if (currentTasks)
|
|
{
|
|
e.Cancel = true;
|
|
|
|
if (Program.RunInAutomatedTestMode ||
|
|
new Dialogs.WarningDialogs.CloseXenCenterWarningDialog().ShowDialog(this) == DialogResult.OK)
|
|
{
|
|
this.Hide();
|
|
|
|
// Close all open forms
|
|
List<Form> forms = new List<Form>();
|
|
foreach (Form form in Application.OpenForms)
|
|
{
|
|
if (form != this)
|
|
{
|
|
forms.Add(form);
|
|
}
|
|
}
|
|
foreach (Form form in forms)
|
|
{
|
|
form.Close();
|
|
}
|
|
|
|
// Disconnect the named pipe
|
|
Program.DisconnectPipe();
|
|
foreach (ActionBase a in ConnectionsManager.History)
|
|
{
|
|
if(!Program.RunInAutomatedTestMode)
|
|
{
|
|
if (a is AsyncAction)
|
|
{
|
|
AsyncAction aa = (AsyncAction) a;
|
|
aa.PrepareForLogReloadAfterRestart();
|
|
}
|
|
|
|
if (!a.IsCompleted && a.CanCancel && !a.SafeToExit)
|
|
a.Cancel();
|
|
}
|
|
else
|
|
{
|
|
if (!a.IsCompleted && a.CanCancel)
|
|
a.Cancel();
|
|
}
|
|
}
|
|
ThreadPool.QueueUserWorkItem(CloseWhenActionsCanceled);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Disconnect the named pipe
|
|
Program.DisconnectPipe();
|
|
|
|
Properties.Settings.Default.WindowSize = this.Size;
|
|
Properties.Settings.Default.WindowLocation = this.Location;
|
|
try
|
|
{
|
|
Settings.SaveServerList();
|
|
Properties.Settings.Default.Save();
|
|
}
|
|
catch (ConfigurationErrorsException ex)
|
|
{
|
|
new ThreeButtonDialog(
|
|
new ThreeButtonDialog.Details(
|
|
SystemIcons.Error,
|
|
string.Format(Messages.MESSAGEBOX_SAVE_CORRUPTED, Settings.GetUserConfigPath()),
|
|
Messages.MESSAGEBOX_SAVE_CORRUPTED_TITLE)).ShowDialog(this);
|
|
log.Error("Couldn't save settings");
|
|
log.Error(ex, ex);
|
|
}
|
|
base.OnClosing(e);
|
|
}
|
|
|
|
private void sendCtrlAltDelToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
ConsolePanel.SendCAD();
|
|
}
|
|
|
|
#region IMainWindowCommandInterface Members
|
|
|
|
/// <summary>
|
|
/// Closes all per-Connection and per-VM wizards for the given connection.
|
|
/// </summary>
|
|
/// <param name="connection"></param>
|
|
public void CloseActiveWizards(IXenConnection connection)
|
|
{
|
|
Program.Invoke(Program.MainWindow, delegate
|
|
{
|
|
// Close and remove any active wizards for any VMs
|
|
foreach (VM vm in connection.Cache.VMs)
|
|
{
|
|
CloseActiveWizards(vm);
|
|
}
|
|
closeActivePoolWizards(connection);
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Closes all per-Connection wizards.
|
|
/// </summary>
|
|
/// <param name="connection"></param>
|
|
private void closeActivePoolWizards(IXenConnection connection)
|
|
{
|
|
IList<Form> wizards;
|
|
if (activePoolWizards.TryGetValue(connection, out wizards))
|
|
{
|
|
foreach (var wizard in wizards)
|
|
{
|
|
if (!wizard.IsDisposed)
|
|
{
|
|
wizard.Close();
|
|
}
|
|
}
|
|
|
|
activePoolWizards.Remove(connection);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Closes all per-XenObject wizards.
|
|
/// </summary>
|
|
/// <param name="obj"></param>
|
|
public void CloseActiveWizards(IXenObject obj)
|
|
{
|
|
Program.Invoke(Program.MainWindow, delegate
|
|
{
|
|
Form wizard;
|
|
if (activeXenModelObjectWizards.TryGetValue(obj, out wizard))
|
|
{
|
|
if (!wizard.IsDisposed)
|
|
{
|
|
wizard.Close();
|
|
}
|
|
activeXenModelObjectWizards.Remove(obj);
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show the given wizard, and impose a one-wizard-per-XenObject limit.
|
|
/// </summary>
|
|
/// <param name="obj">The relevant VM</param>
|
|
/// <param name="wizard">The new wizard to show</param>
|
|
public void ShowPerXenModelObjectWizard(IXenObject obj, Form wizard)
|
|
{
|
|
CloseActiveWizards(obj);
|
|
activeXenModelObjectWizards.Add(obj, wizard);
|
|
wizard.Show(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show the given wizard, and impose a one-wizard-per-connection limit.
|
|
/// </summary>
|
|
/// <param name="connection">The connection. May be null, in which case the wizard
|
|
/// is not addded to any dictionary. This should happen iff this is the New Pool Wizard.</param>
|
|
/// <param name="wizard">The new wizard to show. May not be null.</param>
|
|
public void ShowPerConnectionWizard(IXenConnection connection, Form wizard)
|
|
{
|
|
if (connection != null)
|
|
{
|
|
|
|
if (activePoolWizards.ContainsKey(connection))
|
|
{
|
|
var w = activePoolWizards[connection].Where(x => x.GetType() == wizard.GetType()).FirstOrDefault();
|
|
if (w != null && !w.IsDisposed)
|
|
{
|
|
if (w.WindowState == FormWindowState.Minimized)
|
|
{
|
|
w.WindowState = FormWindowState.Normal;
|
|
}
|
|
w.Focus();
|
|
return;
|
|
}
|
|
if (w != null && w.IsDisposed)
|
|
activePoolWizards[connection].Remove(w);
|
|
|
|
}
|
|
|
|
//closeActivePoolWizards(connection);
|
|
if (activePoolWizards.ContainsKey(connection))
|
|
activePoolWizards[connection].Add(wizard);
|
|
else
|
|
activePoolWizards.Add(connection, new List<Form>() { wizard });
|
|
|
|
}
|
|
|
|
if (!wizard.Disposing && !wizard.IsDisposed && !Program.Exiting)
|
|
{
|
|
wizard.Show(this);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shows a form of the specified type if it has already been created. If the form doesn't exist yet
|
|
/// it is created first and then shown.
|
|
/// </summary>
|
|
/// <param name="type">The type of the form to be shown.</param>
|
|
public void ShowForm(Type type)
|
|
{
|
|
ShowForm(type, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shows a form of the specified type if it has already been created. If the form doesn't exist yet
|
|
/// it is created first and then shown.
|
|
/// </summary>
|
|
/// <param name="type">The type of the form to be shown.</param>
|
|
/// <param name="args">The arguments to pass to the form's consructor</param>
|
|
public void ShowForm(Type type, object[] args)
|
|
{
|
|
foreach (Form form in Application.OpenForms)
|
|
{
|
|
if (form.GetType() == type)
|
|
{
|
|
HelpersGUI.BringFormToFront(form);
|
|
return;
|
|
}
|
|
}
|
|
|
|
Form newForm = (Form)Activator.CreateInstance(type, args);
|
|
newForm.Show(this);
|
|
}
|
|
|
|
public Form Form
|
|
{
|
|
get { return this; }
|
|
}
|
|
|
|
public void Invoke(MethodInvoker method)
|
|
{
|
|
Program.Invoke(this, method);
|
|
}
|
|
|
|
public bool SelectObjectInTree(IXenObject xenObject)
|
|
{
|
|
return SelectObject(xenObject);
|
|
}
|
|
|
|
public Collection<IXenConnection> GetXenConnectionsCopy()
|
|
{
|
|
return new Collection<IXenConnection>(ConnectionsManager.XenConnectionsCopy);
|
|
}
|
|
|
|
public void SaveServerList()
|
|
{
|
|
Settings.SaveServerList();
|
|
}
|
|
|
|
public bool RunInAutomatedTestMode
|
|
{
|
|
get { return Program.RunInAutomatedTestMode; }
|
|
}
|
|
|
|
public void RemoveConnection(IXenConnection connection)
|
|
{
|
|
ConnectionsManager.ClearCacheAndRemoveConnection(connection);
|
|
}
|
|
|
|
public void PutSelectedNodeIntoEditMode()
|
|
{
|
|
EditSelectedNodeInTreeView();
|
|
}
|
|
|
|
|
|
public void TrySelectNewObjectInTree(Predicate<object> tagMatch, bool selectNode, bool expandNode, bool ensureNodeVisible)
|
|
{
|
|
TrySelectNewNode(tagMatch, selectNode, expandNode, ensureNodeVisible);
|
|
}
|
|
|
|
public void TrySelectNewObjectInTree(IXenConnection c, bool selectNode, bool expandNode, bool ensureNodeVisible)
|
|
{
|
|
TrySelectNewNode(c, selectNode, expandNode, ensureNodeVisible);
|
|
}
|
|
|
|
public bool MenuShortcutsEnabled
|
|
{
|
|
get { return _menuShortcuts; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Help
|
|
|
|
private string TabHelpID()
|
|
{
|
|
string modelObj = getSelectedXenModelObjectType();
|
|
|
|
if (TheTabControl.SelectedTab == TabPageHome)
|
|
return "TabPageHome" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageSearch)
|
|
return "TabPageSearch" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageConsole)
|
|
return "TabPageConsole" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageGeneral)
|
|
return "TabPageSettings" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPagePhysicalStorage || TheTabControl.SelectedTab == TabPageStorage || TheTabControl.SelectedTab == TabPageSR)
|
|
return "TabPageStorage" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageNetwork)
|
|
return "TabPageNetwork" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageNICs)
|
|
return "TabPageNICs" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageWLB)
|
|
return "TabPageWLB" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPagePeformance)
|
|
return "TabPagePerformance" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageHA)
|
|
return "TabPageHA" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageHAUpsell)
|
|
return "TabPageHAUpsell" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageSnapshots)
|
|
return "TabPageSnapshots" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageBallooning)
|
|
return "TabPageBallooning" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageAD)
|
|
return "TabPageAD" + modelObj;
|
|
if (TheTabControl.SelectedTab == TabPageBallooningUpsell)
|
|
return "TabPageBallooningUpsell";
|
|
if (TheTabControl.SelectedTab == TabPageWLBUpsell)
|
|
return "TabPageWLBUpsell";
|
|
if (TheTabControl.SelectedTab == TabPageGPU)
|
|
return "TabPageGPU" + modelObj;
|
|
return "TabPageUnknown";
|
|
}
|
|
|
|
private string getSelectedXenModelObjectType()
|
|
{
|
|
// for now, since there are few topics which depend on the selected object we shall just check the special cases
|
|
// when more topic are added we can just return the ModelObjectName
|
|
|
|
if (TheTabControl.SelectedTab == TabPageGeneral && SelectionManager.Selection.First is VM)
|
|
{
|
|
return "VM";
|
|
}
|
|
|
|
if (TheTabControl.SelectedTab == TabPagePhysicalStorage || TheTabControl.SelectedTab == TabPageStorage || TheTabControl.SelectedTab == TabPageSR)
|
|
{
|
|
if (SelectionManager.Selection.FirstIsPool)
|
|
return "Pool";
|
|
if (SelectionManager.Selection.FirstIsHost)
|
|
return "Server";
|
|
if (SelectionManager.Selection.FirstIsVM)
|
|
return "VM";
|
|
if (SelectionManager.Selection.FirstIsSR)
|
|
return "Storage";
|
|
}
|
|
|
|
if (TheTabControl.SelectedTab == TabPageNetwork)
|
|
{
|
|
if (SelectionManager.Selection.FirstIsHost)
|
|
return "Server";
|
|
if (SelectionManager.Selection.FirstIsVM)
|
|
return "VM";
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
public void ShowHelpTOC()
|
|
{
|
|
ShowHelpTopic(null);
|
|
}
|
|
|
|
public void ShowHelpTopic(string topicID)
|
|
{
|
|
// Abandon all hope, ye who enter here: if you're ever tempted to directly invoke hh.exe, see first:
|
|
// JAXC-43: Online help doesn't work if install XenCenter into the path that contains special characters.
|
|
// hh.exe can't seem to cope with certain multi-byte characters in the path to the chm.
|
|
// System.Windows.Forms.Help.ShowHelp() can cope with the special characters in the path, but has the
|
|
// irritating behaviour that the launched help is always on top of the app window (CA-8863).
|
|
// So we show the help 'on top' of an invisible dummy Form.
|
|
|
|
using (var helpForm = new Form())
|
|
{
|
|
string chm = Path.Combine(Program.AssemblyDir, InvisibleMessages.MAINWINDOW_HELP_PATH);
|
|
if (topicID == null)
|
|
{
|
|
// Show TOC
|
|
System.Windows.Forms.Help.ShowHelp(helpForm, chm, HelpNavigator.TableOfContents);
|
|
}
|
|
else
|
|
{
|
|
System.Windows.Forms.Help.ShowHelp(helpForm, chm, HelpNavigator.TopicId, topicID);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void MainWindow_HelpRequested(object sender, HelpEventArgs hlpevent)
|
|
{
|
|
// CA-28064. MessageBox hack to kill the hlpevent it passes to MainWindows.
|
|
if (Program.MainWindow.ContainsFocus && _menuShortcuts)
|
|
LaunchHelp();
|
|
}
|
|
|
|
private void helpTopicsToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
ShowHelpTOC();
|
|
}
|
|
|
|
private void helpContextMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
LaunchHelp();
|
|
}
|
|
|
|
private void LaunchHelp()
|
|
{
|
|
if (alertPage.Visible)
|
|
Help.HelpManager.Launch("AlertSummaryDialog");
|
|
else if (updatesPage.Visible)
|
|
Help.HelpManager.Launch("ManageUpdatesDialog");
|
|
else if (eventsPage.Visible)
|
|
Help.HelpManager.Launch("EventsPane");
|
|
else
|
|
{
|
|
if (TheTabControl.SelectedTab.Tag is TabPageFeature && ((TabPageFeature)TheTabControl.SelectedTab.Tag).HasHelp)
|
|
((TabPageFeature)TheTabControl.SelectedTab.Tag).LaunchHelp();
|
|
else
|
|
Help.HelpManager.Launch(TabHelpID());
|
|
}
|
|
}
|
|
|
|
public bool HasHelp()
|
|
{
|
|
return Help.HelpManager.HasHelpFor(TabHelpID());
|
|
}
|
|
|
|
private void viewApplicationLogToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
Program.ViewLogFiles();
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Used to select the pool or standalone host node for the specified connection which is about to appear in the tree.
|
|
/// </summary>
|
|
/// <param name="connection">The connection.</param>
|
|
/// <param name="selectNode">if set to <c>true</c> then the pool/standalone host node will be selected.</param>
|
|
/// <param name="expandNode">if set to <c>true</c> then the pool/standalone host node will be expanded.</param>
|
|
/// <param name="ensureNodeVisible">if set to <c>true</c> then the matched node will be made visible.</param>
|
|
public void TrySelectNewNode(IXenConnection connection, bool selectNode, bool expandNode, bool ensureNodeVisible)
|
|
{
|
|
if (connection != null)
|
|
{
|
|
TrySelectNewNode(delegate(object o)
|
|
{
|
|
if (o == null)
|
|
{
|
|
return false;
|
|
}
|
|
else if (o.Equals(Helpers.GetPool(connection)))
|
|
{
|
|
return true;
|
|
}
|
|
Host[] hosts = connection.Cache.Hosts;
|
|
return hosts.Length > 0 && o.Equals(hosts[0]);
|
|
}, selectNode, expandNode, ensureNodeVisible);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to select or expand a node which is about to appear in the tree. This is used so that new hosts, folders, pools
|
|
/// etc. can be picked and then selected/expanded.
|
|
///
|
|
/// It fires off a new thread and then repeatedly tries to select a node which matches the specified match
|
|
/// delegate. It stops if it times out or is successful.
|
|
/// </summary>
|
|
/// <param name="tagMatch">A match for the tag of the node.</param>
|
|
/// <param name="selectNode">if set to <c>true</c> then the matched node will be selected.</param>
|
|
/// <param name="expandNode">if set to <c>true</c> then the matched node will be expanded.</param>
|
|
/// <param name="ensureNodeVisible">if set to <c>true</c> then the matched node will be made visible.</param>
|
|
public void TrySelectNewNode(Predicate<object> tagMatch, bool selectNode, bool expandNode, bool ensureNodeVisible)
|
|
{
|
|
ThreadPool.QueueUserWorkItem(delegate
|
|
{
|
|
bool success = false;
|
|
|
|
for (int i = 0; i < 20 && !success; i++)
|
|
{
|
|
Program.Invoke(Program.MainWindow, delegate
|
|
{
|
|
success = navigationPane.TryToSelectNewNode(tagMatch, selectNode, expandNode, ensureNodeVisible);
|
|
});
|
|
Thread.Sleep(500);
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Selects the specified object in the treeview.
|
|
/// </summary>
|
|
/// <param name="o">The object to be selected.</param>
|
|
/// <returns>A value indicating whether selection was successful.</returns>
|
|
public bool SelectObject(IXenObject o)
|
|
{
|
|
return navigationPane.SelectObject(o);
|
|
}
|
|
|
|
private void eventsPage_GoToXenObjectRequested(IXenObject obj)
|
|
{
|
|
navigationPane.SwitchToInfrastructureMode();
|
|
navigationPane.SelectObject(obj);
|
|
}
|
|
|
|
private void Updates_CollectionChanged(object sender, CollectionChangeEventArgs e)
|
|
{
|
|
Program.Invoke(this, () =>
|
|
{
|
|
int updatesCount = Updates.UpdateAlertsCount;
|
|
navigationPane.UpdateNotificationsButton(NotificationsSubMode.Updates, updatesCount);
|
|
|
|
if (updatesPage.Visible)
|
|
{
|
|
TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Updates, updatesCount);
|
|
TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Updates, updatesCount);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void CloseWhenActionsCanceled(object o)
|
|
{
|
|
int i = 0;
|
|
while (true)
|
|
{
|
|
if (i > 20)
|
|
Program.ForcedExiting = true;
|
|
|
|
if (i > 40 || AllActionsCompleted())
|
|
{
|
|
Program.Invoke(this, Application.Exit);
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
System.Threading.Thread.Sleep(500);
|
|
}
|
|
}
|
|
|
|
private bool AllActionsCompleted()
|
|
{
|
|
foreach (ActionBase a in ConnectionsManager.History)
|
|
{
|
|
if (!a.IsCompleted)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void preferencesToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
OptionsDialog dialog = new OptionsDialog(pluginManager);
|
|
dialog.ShowDialog(this);
|
|
}
|
|
|
|
internal void action_Completed(ActionBase sender)
|
|
{
|
|
if (Program.Exiting)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RequestRefreshTreeView();
|
|
|
|
//Update toolbars, since if an action has just completed, various
|
|
//buttons may need to be re-enabled. Applies to:
|
|
// HostAction
|
|
// EnableHAAction
|
|
// DisableHAAction
|
|
Program.Invoke(this, UpdateToolbars);
|
|
}
|
|
|
|
internal void OpenGlobalImportWizard(string param)
|
|
{
|
|
HelpersGUI.BringFormToFront(this);
|
|
Host hostAncestor = SelectionManager.Selection.Count == 1 ? SelectionManager.Selection[0].HostAncestor : null;
|
|
new ImportWizard(SelectionManager.Selection.GetConnectionOfFirstItem(), hostAncestor, param, false).Show();
|
|
}
|
|
|
|
internal void InstallUpdate(string path)
|
|
{
|
|
var wizard = new PatchingWizard();
|
|
wizard.Show(this);
|
|
wizard.NextStep();
|
|
wizard.AddFile(path);
|
|
}
|
|
|
|
#region XenSearch
|
|
|
|
// SearchMode doesn't just mean we are looking at the Search tab.
|
|
// It's set when we import a search from a file; or when we double-click
|
|
// on a folder or tag name to search for it.
|
|
private bool searchMode = false;
|
|
public bool SearchMode
|
|
{
|
|
get
|
|
{
|
|
return searchMode;
|
|
}
|
|
set
|
|
{
|
|
if (searchMode == value)
|
|
return;
|
|
|
|
searchMode = value;
|
|
navigationPane.InSearchMode = value;
|
|
UpdateToolbarsCore();
|
|
}
|
|
}
|
|
|
|
public bool DoSearch(string filename)
|
|
{
|
|
List<Search> searches = Search.LoadFile(filename);
|
|
if (searches != null && searches.Count > 0)
|
|
{
|
|
Program.Invoke(Program.MainWindow, delegate()
|
|
{
|
|
DoSearch(searches[0]);
|
|
});
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void DoSearch(Search search)
|
|
{
|
|
History.NewHistoryItem(new SearchHistoryItem(search));
|
|
|
|
SearchMode = true;
|
|
SearchPage.Search = search;
|
|
|
|
UpdateHeader();
|
|
}
|
|
|
|
public void SearchForTag(string tag)
|
|
{
|
|
DoSearch(Search.SearchForTag(tag));
|
|
}
|
|
|
|
public void SearchForFolder(string path)
|
|
{
|
|
DoSearch(Search.SearchForFolder(path));
|
|
}
|
|
|
|
void SearchPanel_SearchChanged()
|
|
{
|
|
if (SearchMode)
|
|
History.ReplaceHistoryItem(new SearchHistoryItem(SearchPage.Search));
|
|
else
|
|
History.ReplaceHistoryItem(new ModifiedSearchHistoryItem(
|
|
SelectionManager.Selection.FirstAsXenObject, SearchPage.Search));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the shiny gradient bar with selected object name and icon.
|
|
/// Also updates 'Logged in as:'.
|
|
/// </summary>
|
|
private void UpdateHeader()
|
|
{
|
|
if (!TheTabControl.Visible)
|
|
return;
|
|
|
|
if (SearchMode && SearchPage.Search != null)
|
|
{
|
|
TitleLabel.Text = HelpersGUI.GetLocalizedSearchName(SearchPage.Search);
|
|
TitleIcon.Image = Images.GetImage16For(SearchPage.Search);
|
|
}
|
|
else if (!SearchMode && SelectionManager.Selection.ContainsOneItemOfType<IXenObject>())
|
|
{
|
|
IXenObject xenObject = SelectionManager.Selection[0].XenObject;
|
|
TitleLabel.Text = GetTitleLabel(xenObject);
|
|
TitleIcon.Image = Images.GetImage16For(xenObject);
|
|
// When in folder view only show the logged in label if it is clear to which connection the object belongs (most likely pools and hosts)
|
|
|
|
if (SelectionManager.Selection[0].PoolAncestor == null && SelectionManager.Selection[0].HostAncestor == null)
|
|
loggedInLabel1.Connection = null;
|
|
else
|
|
loggedInLabel1.Connection = xenObject.Connection;
|
|
}
|
|
else
|
|
{
|
|
TitleLabel.Text = Messages.XENCENTER;
|
|
TitleIcon.Image = Properties.Resources.Logo;
|
|
loggedInLabel1.Connection = null;
|
|
}
|
|
}
|
|
|
|
private void UpdateViewMenu(NavigationPane.NavigationMode mode)
|
|
{
|
|
//the order is the reverse from the order in which we want them to appear
|
|
var items = new ToolStripItem []
|
|
{
|
|
toolStripSeparator24,
|
|
ShowHiddenObjectsToolStripMenuItem,
|
|
localStorageToolStripMenuItem,
|
|
templatesToolStripMenuItem1,
|
|
customTemplatesToolStripMenuItem
|
|
};
|
|
|
|
if (mode == NavigationPane.NavigationMode.Infrastructure)
|
|
{
|
|
foreach (var item in items)
|
|
{
|
|
if (!viewToolStripMenuItem.DropDownItems.Contains(item))
|
|
viewToolStripMenuItem.DropDownItems.Insert(0, item);
|
|
}
|
|
}
|
|
else if (mode == NavigationPane.NavigationMode.Notifications)
|
|
{
|
|
foreach (var item in items)
|
|
viewToolStripMenuItem.DropDownItems.Remove(item);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 2; i < items.Length; i++)
|
|
viewToolStripMenuItem.DropDownItems.Remove(items[i]);
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
if (!viewToolStripMenuItem.DropDownItems.Contains(items[i]))
|
|
viewToolStripMenuItem.DropDownItems.Insert(0, items[i]);
|
|
}
|
|
|
|
pluginMenuItemStartIndexes[viewToolStripMenuItem] = viewToolStripMenuItem.DropDownItems.IndexOf(toolStripSeparator24) + 1;
|
|
}
|
|
|
|
string GetTitleLabel(IXenObject xenObject)
|
|
{
|
|
string name = Helpers.GetName(xenObject);
|
|
VM vm = xenObject as VM;
|
|
if (vm != null && vm.is_a_real_vm)
|
|
{
|
|
Host server = vm.Home();
|
|
if (server != null)
|
|
return string.Format(Messages.VM_ON_SERVER, name, server);
|
|
Pool pool = Helpers.GetPool(vm.Connection);
|
|
if (pool != null)
|
|
return string.Format(Messages.VM_IN_POOL, name, pool);
|
|
}
|
|
return name;
|
|
}
|
|
|
|
void navigationPane_DragDropCommandActivated(string cmdText)
|
|
{
|
|
SetStatusBar(null, cmdText);
|
|
}
|
|
|
|
private void navigationPane_TreeViewSelectionChanged()
|
|
{
|
|
UpdateToolbars();
|
|
|
|
//
|
|
// NB do not trigger updates to the panels in this method
|
|
// instead, put them in TheTabControl_SelectedIndexChanged,
|
|
// so only the selected tab is updated
|
|
//
|
|
|
|
TheTabControl_SelectedIndexChanged(null, EventArgs.Empty);
|
|
|
|
if (TheTabControl.SelectedTab != null)
|
|
TheTabControl.SelectedTab.Refresh();
|
|
|
|
UpdateHeader();
|
|
}
|
|
|
|
private void navigationPane_NotificationsSubModeChanged(NotificationsSubModeItem submodeItem)
|
|
{
|
|
alertPage.Visible = submodeItem.SubMode == NotificationsSubMode.Alerts;
|
|
updatesPage.Visible = submodeItem.SubMode == NotificationsSubMode.Updates;
|
|
eventsPage.Visible = submodeItem.SubMode == NotificationsSubMode.Events;
|
|
TheTabControl.Visible = false;
|
|
|
|
if (alertPage.Visible)
|
|
alertPage.RefreshAlertList();
|
|
|
|
if (updatesPage.Visible)
|
|
updatesPage.RefreshUpdateList();
|
|
|
|
if (eventsPage.Visible)
|
|
{
|
|
// Unmark node if user has now seen error in log tab
|
|
if (SelectionManager.Selection.FirstAsXenObject != null)
|
|
SelectionManager.Selection.FirstAsXenObject.InError = false;
|
|
|
|
eventsPage.RefreshDisplayedEvents();
|
|
RequestRefreshTreeView();
|
|
}
|
|
|
|
loggedInLabel1.Connection = null;
|
|
TitleLabel.Text = submodeItem.Text;
|
|
TitleIcon.Image = submodeItem.Image;
|
|
}
|
|
|
|
private void navigationPane_NavigationModeChanged(NavigationPane.NavigationMode mode)
|
|
{
|
|
if (mode == NavigationPane.NavigationMode.Notifications)
|
|
{
|
|
TheTabControl.Visible = false;
|
|
}
|
|
else
|
|
{
|
|
bool tabControlWasVisible = TheTabControl.Visible;
|
|
TheTabControl.Visible = true;
|
|
alertPage.Visible = updatesPage.Visible = eventsPage.Visible = false;
|
|
|
|
// force an update of the selected tab when switching back from Notification view,
|
|
// as some tabs ignore the update events when not visible (e.g. Snapshots, HA)
|
|
if (!tabControlWasVisible)
|
|
TheTabControl_SelectedIndexChanged(null, null);
|
|
}
|
|
|
|
UpdateViewMenu(mode);
|
|
}
|
|
|
|
private void navigationPane_TreeNodeBeforeSelected()
|
|
{
|
|
SearchMode = false;
|
|
}
|
|
|
|
private void navigationPane_TreeNodeClicked()
|
|
{
|
|
if (SearchMode)
|
|
{
|
|
SearchMode = false;
|
|
TheTabControl_SelectedIndexChanged(null, null);
|
|
UpdateHeader();
|
|
}
|
|
}
|
|
|
|
private void navigationPane_TreeNodeRightClicked()
|
|
{
|
|
MainMenuBar_MenuActivate(MainMenuBar, new EventArgs());
|
|
}
|
|
|
|
private void navigationPane_TreeViewRefreshed()
|
|
{
|
|
UpdateHeaderAndTabPages();
|
|
}
|
|
|
|
private void navigationPane_TreeViewRefreshResumed()
|
|
{
|
|
ignoreUpdateToolbars--;
|
|
if (ignoreUpdateToolbars == 0 && calledUpdateToolbars)
|
|
UpdateToolbars();
|
|
}
|
|
|
|
private void navigationPane_TreeViewRefreshSuspended()
|
|
{
|
|
if (ignoreUpdateToolbars == 0)
|
|
calledUpdateToolbars = false;
|
|
ignoreUpdateToolbars++;
|
|
}
|
|
|
|
#endregion
|
|
|
|
void XenCenterAlerts_CollectionChanged(object sender, CollectionChangeEventArgs e)
|
|
{
|
|
Program.BeginInvoke(Program.MainWindow, () =>
|
|
{
|
|
navigationPane.UpdateNotificationsButton(
|
|
NotificationsSubMode.Alerts, Alert.NonDismissingAlertCount);
|
|
|
|
if (alertPage.Visible)
|
|
{
|
|
TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Alerts, Alert.NonDismissingAlertCount);
|
|
TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Alerts, Alert.NonDismissingAlertCount);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void backButton_Click(object sender, EventArgs e)
|
|
{
|
|
History.Back(1);
|
|
}
|
|
|
|
private void forwardButton_Click(object sender, EventArgs e)
|
|
{
|
|
History.Forward(1);
|
|
}
|
|
|
|
private void backButton_DropDownOpening(object sender, EventArgs e)
|
|
{
|
|
ToolStripSplitButton button = sender as ToolStripSplitButton;
|
|
if (button == null)
|
|
return;
|
|
|
|
History.PopulateBackDropDown(button);
|
|
}
|
|
|
|
private void forwardButton_DropDownOpening(object sender, EventArgs e)
|
|
{
|
|
ToolStripSplitButton button = sender as ToolStripSplitButton;
|
|
if (button == null)
|
|
return;
|
|
|
|
History.PopulateForwardDropDown(button);
|
|
}
|
|
|
|
|
|
private void LicenseManagerMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
licenseManagerLauncher.LaunchIfRequired(false, ConnectionsManager.XenConnections, SelectionManager.Selection);
|
|
}
|
|
|
|
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.KeyCode == Keys.F5)
|
|
{
|
|
RequestRefreshTreeView();
|
|
if (TheTabControl.SelectedTab == TabPageSearch)
|
|
SearchPage.PanelProd();
|
|
}
|
|
}
|
|
|
|
private void ShowToolbarMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
ToolbarsEnabled = !ToolbarsEnabled;
|
|
Properties.Settings.Default.ToolbarsEnabled = ToolbarsEnabled;
|
|
UpdateToolbars();
|
|
}
|
|
|
|
private void MainMenuBar_MouseClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Right)
|
|
{
|
|
ToolBarContextMenu.Show(Program.MainWindow, e.Location);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Equivalent to MainController.Confirm(conn, this, msg, args).
|
|
/// </summary>
|
|
public bool Confirm(IXenConnection conn, string title, string msg, params object[] args)
|
|
{
|
|
return Confirm(conn, this, title, msg, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show a MessageBox asking to confirm an operation. The MessageBox will be parented to the given Control.
|
|
/// Displays default "Yes" and "No" buttons ("Yes" button is selected by default).
|
|
/// The args given will be ellipsised to Helpers.DEFAULT_NAME_TRIM_LENGTH, if they are strings.
|
|
/// If in automated test mode, then always returns true.
|
|
/// If the user refuses the operation, then returns false.
|
|
/// If the given connection has disconnected in the time it takes the user to confirm,
|
|
/// then shows an information MessageBox, and returns false.
|
|
/// Otherwise, the user has agreed and the connection is still alive, so
|
|
/// sets MainWindow.AllowHistorySwitch to true and returns true.
|
|
/// </summary>
|
|
public static bool Confirm(IXenConnection conn, Control parent, string title, string msg, params object[] args)
|
|
{
|
|
return Confirm(conn, parent, title, null, null, null, msg, args);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show a MessageBox asking to confirm an operation. The MessageBox will be parented to the given Control.
|
|
/// "Yes" and "No" buttons can be customized.
|
|
/// The args given will be ellipsised to Helpers.DEFAULT_NAME_TRIM_LENGTH, if they are strings.
|
|
/// If in automated test mode, then always returns true.
|
|
/// If the user refuses the operation, then returns false.
|
|
/// If the given connection has disconnected in the time it takes the user to confirm,
|
|
/// then shows an information MessageBox, and returns false.
|
|
/// Otherwise, the user has agreed and the connection is still alive, so
|
|
/// sets MainWindow.AllowHistorySwitch to true and returns true.
|
|
/// </summary>
|
|
public static bool Confirm(IXenConnection conn, Control parent, string title,
|
|
string helpName, ThreeButtonDialog.TBDButton buttonYes, ThreeButtonDialog.TBDButton buttonNo,
|
|
string msg, params object[] args)
|
|
{
|
|
if (Program.RunInAutomatedTestMode)
|
|
return true;
|
|
|
|
Trim(args);
|
|
|
|
var buttons = new[]
|
|
{
|
|
buttonYes ?? ThreeButtonDialog.ButtonYes,
|
|
buttonNo ?? ThreeButtonDialog.ButtonNo
|
|
};
|
|
|
|
var details = new ThreeButtonDialog.Details(SystemIcons.Exclamation,
|
|
args.Length == 0 ? msg : string.Format(msg, args), title);
|
|
|
|
var dialog = String.IsNullOrEmpty(helpName)
|
|
? new ThreeButtonDialog(details, buttons)
|
|
: new ThreeButtonDialog(details, helpName, buttons);
|
|
|
|
if (dialog.ShowDialog(parent ?? Program.MainWindow) != DialogResult.Yes)
|
|
return false;
|
|
|
|
|
|
if (conn != null && !conn.IsConnected)
|
|
{
|
|
ShowDisconnectedMessage(parent);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show a message telling the user that the connection has disappeared. We check this after
|
|
/// we've shown a dialog, in case it's happened in the time it took them to click OK.
|
|
/// </summary>
|
|
public static void ShowDisconnectedMessage(Control parent)
|
|
{
|
|
// We could have done some teardown by now, so we need to be paranoid about things going away
|
|
// beneath us.
|
|
if (Program.Exiting)
|
|
return;
|
|
if (parent == null || parent.Disposing || parent.IsDisposed)
|
|
{
|
|
parent = Program.MainWindow;
|
|
if (parent.Disposing || parent.IsDisposed)
|
|
return;
|
|
}
|
|
new ThreeButtonDialog(
|
|
new ThreeButtonDialog.Details(
|
|
SystemIcons.Warning,
|
|
Messages.DISCONNECTED_BEFORE_ACTION_STARTED,
|
|
Messages.XENCENTER)).ShowDialog(parent);
|
|
}
|
|
|
|
private static void Trim(object[] args)
|
|
{
|
|
int n = args.Length;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
if (args[i] is string)
|
|
args[i] = ((string)args[i]).Ellipsise(Helpers.DEFAULT_NAME_TRIM_LENGTH);
|
|
}
|
|
}
|
|
|
|
#region ISynchronizeInvoke Members
|
|
|
|
// this explicit implementation of ISynchronizeInvoke is used to allow the model to update
|
|
// its API on the main program thread while being decoupled from MainWindow.
|
|
|
|
// See StorageLinkConnection for an example of its usage.
|
|
|
|
IAsyncResult ISynchronizeInvoke.BeginInvoke(Delegate method, object[] args)
|
|
{
|
|
return Program.BeginInvoke(this, method, args);
|
|
}
|
|
|
|
object ISynchronizeInvoke.EndInvoke(IAsyncResult result)
|
|
{
|
|
return EndInvoke(result);
|
|
}
|
|
|
|
object ISynchronizeInvoke.Invoke(Delegate method, object[] args)
|
|
{
|
|
return Program.Invoke(this, method, args);
|
|
}
|
|
|
|
bool ISynchronizeInvoke.InvokeRequired
|
|
{
|
|
get { return InvokeRequired; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void importSettingsToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
using (OpenFileDialog dialog = new OpenFileDialog())
|
|
{
|
|
dialog.Filter = Messages.XENCENTER_CONFIG_FILTER;
|
|
if (dialog.ShowDialog(this) != DialogResult.Cancel)
|
|
{
|
|
try
|
|
{
|
|
log.InfoFormat("Importing server list from '{0}'", dialog.FileName);
|
|
|
|
XmlDocument xmlDocument = new XmlDocument();
|
|
using (var stream = dialog.OpenFile())
|
|
xmlDocument.Load(stream);
|
|
|
|
foreach (XmlNode itemConnection in xmlDocument.GetElementsByTagName("XenConnection"))
|
|
{
|
|
var conn = new XenConnection();
|
|
foreach (XmlNode item in itemConnection.ChildNodes)
|
|
{
|
|
|
|
switch (item.Name)
|
|
{
|
|
case "Hostname":
|
|
conn.Hostname = item.InnerText;
|
|
break;
|
|
case "Port":
|
|
conn.Port = int.Parse(item.InnerText);
|
|
break;
|
|
case "FriendlyName":
|
|
conn.FriendlyName = item.InnerText;
|
|
break;
|
|
}
|
|
}
|
|
if (null == ConnectionsManager.XenConnections.Find(existing => (existing.Hostname == conn.Hostname && existing.Port == conn.Port)))
|
|
ConnectionsManager.XenConnections.Add(conn);
|
|
RequestRefreshTreeView();
|
|
}
|
|
|
|
log.InfoFormat("Imported server list from '{0}' successfully.", dialog.FileName);
|
|
}
|
|
catch (XmlException)
|
|
{
|
|
log.ErrorFormat("Failed to import server list from '{0}'", dialog.FileName);
|
|
|
|
new ThreeButtonDialog(
|
|
new ThreeButtonDialog.Details(SystemIcons.Error, Messages.ERRO_IMPORTING_SERVER_LIST, Messages.XENCENTER))
|
|
.ShowDialog(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void exportSettingsToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
using (SaveFileDialog dialog = new SaveFileDialog())
|
|
{
|
|
dialog.Filter = Messages.XENCENTER_CONFIG_FILTER;
|
|
dialog.Title = Messages.ACTION_SAVE_CHANGES_IN_PROGRESS;
|
|
dialog.CheckPathExists = true;
|
|
if (dialog.ShowDialog(this) != DialogResult.Cancel)
|
|
{
|
|
log.InfoFormat("Exporting server list to '{0}'", dialog.FileName);
|
|
|
|
try
|
|
{
|
|
using (var xmlWriter = new XmlTextWriter(dialog.OpenFile(), Encoding.Unicode))
|
|
{
|
|
xmlWriter.WriteStartDocument();
|
|
xmlWriter.WriteStartElement("XenConnections");
|
|
xmlWriter.WriteWhitespace("\n");
|
|
|
|
foreach (var connection in ConnectionsManager.XenConnections)
|
|
{
|
|
xmlWriter.WriteStartElement("XenConnection");
|
|
{
|
|
xmlWriter.WriteElementString("Hostname", connection.Hostname);
|
|
xmlWriter.WriteElementString("Port", connection.Port.ToString());
|
|
xmlWriter.WriteWhitespace("\n ");
|
|
xmlWriter.WriteElementString("FriendlyName", connection.FriendlyName);
|
|
}
|
|
xmlWriter.WriteEndElement();
|
|
xmlWriter.WriteWhitespace("\n");
|
|
}
|
|
|
|
xmlWriter.WriteEndElement();
|
|
xmlWriter.WriteEndDocument();
|
|
}
|
|
|
|
log.InfoFormat("Exported server list to '{0}' successfully.", dialog.FileName);
|
|
}
|
|
catch
|
|
{
|
|
log.ErrorFormat("Failed to export server list to '{0}'.", dialog.FileName);
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void MainWindow_Load(object sender, EventArgs e)
|
|
{
|
|
SetSplitterDistance();
|
|
}
|
|
|
|
private void MainWindow_Resize(object sender, EventArgs e)
|
|
{
|
|
SetSplitterDistance();
|
|
}
|
|
|
|
private void SetSplitterDistance()
|
|
{
|
|
//CA-71697: chosen min size so the tab contents are visible
|
|
int chosenPanel2MinSize = MinimumSize.Width / 2;
|
|
int min = splitContainer1.Panel1MinSize;
|
|
int max = splitContainer1.Width - chosenPanel2MinSize;
|
|
|
|
if (max < min)
|
|
return;
|
|
|
|
splitContainer1.Panel2MinSize = chosenPanel2MinSize;
|
|
|
|
if (splitContainer1.SplitterDistance < min)
|
|
splitContainer1.SplitterDistance = min;
|
|
else if (splitContainer1.SplitterDistance > max)
|
|
splitContainer1.SplitterDistance = max;
|
|
}
|
|
}
|
|
}
|