xenadmin/XenAdmin/MainWindow.cs

3388 lines
140 KiB
C#
Raw Normal View History

/* 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 XenCenterLib;
using System.Linq;
using XenAdmin.Wizards;
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 ConsolePanel ConsolePanel = new ConsolePanel();
internal readonly CvmConsolePanel CvmConsolePanel = new CvmConsolePanel();
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 ADUpsellPage AdUpsellPage = new ADUpsellPage();
internal readonly GpuPage GpuPage = new GpuPage();
internal readonly PvsPage PvsPage = new PvsPage();
internal readonly DockerProcessPage DockerProcessPage = new DockerProcessPage();
internal readonly DockerDetailsPage DockerDetailsPage = new DockerDetailsPage();
internal readonly UsbPage UsbPage = new UsbPage();
private ActionBase statusBarAction = null;
private bool IgnoreTabChanges = false;
private bool ToolbarsEnabled;
/// <summary>
/// Helper boolean to only trigger Resize_End when window is really resized by dragging edges
/// Without this Resize_End is triggered even when window is moved around and not resized
/// </summary>
private bool mainWindowResized = false;
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;
public readonly HealthCheckOverviewLauncher HealthCheckOverviewLauncher;
private readonly System.Windows.Forms.Timer healthCheckResultTimer = new System.Windows.Forms.Timer();
private Dictionary<ToolStripMenuItem, int> pluginMenuItemStartIndexes = new Dictionary<ToolStripMenuItem, int>();
private bool expandTreeNodesOnStartup;
private int connectionsInProgressOnStartup;
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern uint RegisterApplicationRestart(string pszCommandline, uint dwFlags);
public MainWindow(ArgType argType, string[] args)
{
Program.MainWindow = this;
licenseManagerLauncher = new LicenseManagerLauncher(Program.MainWindow);
HealthCheckOverviewLauncher = new HealthCheckOverviewLauncher(Program.MainWindow);
InvokeHelper.Initialize(this);
InitializeComponent();
SetMenuItemStartIndexes();
Icon = Properties.Resources.AppIcon;
//CA-270999: Add registration to RestartManager
RegisterApplicationRestart(null, 0);
#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(CvmConsolePanel);
components.Add(NetworkPage);
components.Add(HAPage);
components.Add(HomePage);
components.Add(WlbPage);
components.Add(AdPage);
components.Add(GpuPage);
components.Add(PvsPage);
components.Add(SearchPage);
components.Add(DockerProcessPage);
components.Add(DockerDetailsPage);
components.Add(UsbPage);
AddTabContents(VMStoragePage, TabPageStorage);
AddTabContents(SrStoragePage, TabPageSR);
AddTabContents(NICPage, TabPageNICs);
AddTabContents(PerformancePage, TabPagePeformance);
AddTabContents(GeneralPage, TabPageGeneral);
AddTabContents(BallooningPage, TabPageBallooning);
AddTabContents(ConsolePanel, TabPageConsole);
AddTabContents(CvmConsolePanel, TabPageCvmConsole);
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(AdUpsellPage, TabPageADUpsell);
AddTabContents(GpuPage, TabPageGPU);
AddTabContents(PvsPage, TabPagePvs);
AddTabContents(SearchPage, TabPageSearch);
AddTabContents(DockerProcessPage, TabPageDockerProcess);
AddTabContents(DockerDetailsPage, TabPageDockerDetails);
AddTabContents(UsbPage, TabPageUSB);
#endregion
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);
((WinformsXenAdminConfigProvider) XenAdminConfigManager.Provider).PluginManager = pluginManager;
eventsPage.GoToXenObjectRequested += eventsPage_GoToXenObjectRequested;
SearchPage.SearchChanged += SearchPanel_SearchChanged;
Alert.RegisterAlertCollectionChanged(XenCenterAlerts_CollectionChanged);
Updates.RegisterCollectionChanged(Updates_CollectionChanged);
FormFontFixer.Fix(this);
Folders.InitFolders();
DockerContainers.InitDockerContainers();
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;
toolStripSeparator7.Visible = xenSourceOnTheWebToolStripMenuItem.Visible = xenCenterPluginsOnlineToolStripMenuItem.Visible = !HiddenFeatures.ToolStripMenuItemHidden;
healthCheckToolStripMenuItem1.Visible = !HiddenFeatures.HealthCheckHidden;
}
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.FirstIs<Host>())
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);
}
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 = e.Element as ActionBase;
switch (e.Action)
{
case CollectionChangeAction.Add:
{
if (action == null)
return;
var meddlingAction = action as MeddlingAction;
if (meddlingAction == null)
{
SetStatusBar(null, null);
if (statusBarAction != null)
{
statusBarAction.Changed -= actionChanged;
statusBarAction.Completed -= actionCompleted;
}
statusBarAction = action;
}
action.Changed += actionChanged;
action.Completed += actionCompleted;
actionChanged(action);
break;
}
case CollectionChangeAction.Remove:
{
if (action != null)
{
action.Changed -= actionChanged;
action.Completed -= actionCompleted;
}
else
{
var range = e.Element as List<ActionBase>;
if (range != null)
{
foreach (var a in range)
{
a.Changed -= actionChanged;
a.Completed -= actionCompleted;
}
}
else
{
return;
}
}
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;
}
}
});
}
private void actionCompleted(ActionBase action)
{
actionChanged(action);
if (action is SrAction)
Program.Invoke(this, UpdateToolbars);
}
private void actionChanged(ActionBase action)
{
if (Program.Exiting)
return;
Program.Invoke(this, () => actionChanged_(action));
}
private void actionChanged_(ActionBase action)
{
// suppress updates when the PureAsyncAction runs the action to populate the ApiMethodsToRoleCheck
if (action.SuppressProgressReport)
return;
var percentage = action.PercentComplete;
Debug.Assert(0 <= percentage && percentage <= 100,
"PercentComplete is out of range, the reporting action needs to be fixed."); //CA-8517
var meddlingAction = action as MeddlingAction;
if (meddlingAction == null)
{
statusProgressBar.Visible = action.ShowProgress && !action.IsCompleted;
if (percentage < 0)
percentage = 0;
else if (percentage > 100)
percentage = 100;
statusProgressBar.Value = percentage;
// Don't show cancelled exception
if (action.Exception != null && !(action.Exception is CancelledException))
{
SetStatusBar(Properties.Resources._000_error_h32bit_16, action.Exception.Message);
}
else
{
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 && !(a is CancellingAction && ((CancellingAction)a).Cancelled));
navigationPane.UpdateNotificationsButton(NotificationsSubMode.Events, errors);
if (eventsPage.Visible)
{
TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Events, errors);
TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Events, errors);
}
}
private void SetStatusBar(Image image, string message)
{
statusLabel.Image = image;
statusLabel.Text = Helpers.FirstLine(message);
}
private void MainWindow_Shown(object sender, EventArgs e)
{
MainMenuBar.Location = new Point(0, 0);
var rendProf = ToolStrip.Renderer as ToolStripProfessionalRenderer;
if (rendProf != null)
rendProf.RoundedEdges = false;
ConnectionsManager.XenConnections.CollectionChanged += XenConnection_CollectionChanged;
try
{
Settings.RestoreSession();
HealthCheck.SendProxySettingsToHealthCheck();
}
catch (ConfigurationErrorsException ex)
{
log.Error("Could not load settings.", ex);
Program.CloseSplash();
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(
SystemIcons.Error,
string.Format(Messages.MESSAGEBOX_LOAD_CORRUPTED, Settings.GetUserConfigPath()),
Messages.MESSAGEBOX_LOAD_CORRUPTED_TITLE)))
{
dlg.ShowDialog(this);
}
Application.Exit();
return; //return explicitly because Application.Exit() does not exit the current method.
}
ToolbarsEnabled = Properties.Settings.Default.ToolbarsEnabled;
RequestRefreshTreeView();
// if there are fewer than 30 connections, then expand the tree nodes.
expandTreeNodesOnStartup = ConnectionsManager.XenConnectionsCopy.Count < 30;
connectionsInProgressOnStartup = 0;
// kick-off connections for all the loaded server list
foreach (IXenConnection connection in ConnectionsManager.XenConnectionsCopy)
{
if (!connection.SaveDisconnected)
{
connectionsInProgressOnStartup++;
connection.ConnectionStateChanged += Connection_ConnectionStateChangedOnStartup;
connection.CachePopulated += connection_CachePopulatedOnStartup;
XenConnectionUI.BeginConnect(connection, true, this, true);
}
}
ThreadPool.QueueUserWorkItem(delegate
{
// 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);
}
if (!Program.RunInAutomatedTestMode)
{
// start healthCheckResult thread
healthCheckResultTimer.Interval = 1000 * 60 * 60; // 1 hour
healthCheckResultTimer.Tick += HealthCheckResultTimer_Tick;
healthCheckResultTimer.Start();
}
ProcessCommand(CommandLineArgType, CommandLineParam);
}
private void CheckForUpdatesTimer_Tick(object sender, EventArgs e)
{
Updates.CheckForUpdates(false);
}
private void HealthCheckResultTimer_Tick(object sender, EventArgs e)
{
HealthCheck.CheckForAnalysisResults();
}
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 connection_CachePopulatedOnStartup(object sender, EventArgs e)
{
IXenConnection c = (IXenConnection)sender;
c.CachePopulated -= connection_CachePopulatedOnStartup;
if (expandTreeNodesOnStartup)
TrySelectNewNode(c, false, true, false);
Program.Invoke(this, ShowAboutDialogOnStartup);
}
private void Connection_ConnectionStateChangedOnStartup(object sender, EventArgs e)
{
IXenConnection c = (IXenConnection)sender;
c.ConnectionStateChanged -= Connection_ConnectionStateChangedOnStartup;
Program.Invoke(Program.MainWindow, delegate
{
connectionsInProgressOnStartup--;
// show the About dialog if this was the last connection in progress and the connection failed
if (!c.IsConnected)
ShowAboutDialogOnStartup();
});
}
/// <summary>
/// Show the About dialog after all conncections kicked-off on startup have finished the connection phase (cache populated)
/// Must be called on the event thread.
/// </summary>
private void ShowAboutDialogOnStartup()
{
Program.AssertOnEventThread();
if (connectionsInProgressOnStartup > 0)
return;
if (Properties.Settings.Default.ShowAboutDialog && HiddenFeatures.LicenseNagVisible)
ShowForm(typeof(AboutDialog));
}
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:
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 = e.Element as IXenConnection;
navigationPane.XenConnectionCollectionChanged(e);
if (e.Action == CollectionChangeAction.Add)
{
if (connection == null)
return;
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)
{
var range = new List<IXenConnection>();
if (connection != null)
{
range.Add(connection);
}
else
{
var r = e.Element as List<IXenConnection>;
if (r != null)
range = r;
else
return;
}
foreach (var con in range)
{
con.ClearingCache -= connection_ClearingCache;
con.ConnectionResult -= Connection_ConnectionResult;
con.ConnectionLost -= Connection_ConnectionLost;
con.ConnectionClosed -= Connection_ConnectionClosed;
con.ConnectionReconnecting -= connection_ConnectionReconnecting;
con.XenObjectsUpdated -= Connection_XenObjectsUpdated;
con.Cache.DeregisterCollectionChanged<XenAPI.Message>(MessageCollectionChangedWithInvoke);
con.Cache.DeregisterCollectionChanged<Pool>(PoolCollectionChangedWithInvoke);
con.Cache.DeregisterCollectionChanged<Host>(HostCollectionChangedWithInvoke);
con.Cache.DeregisterCollectionChanged<VM>(VMCollectionChangedWithInvoke);
con.Cache.DeregisterCollectionChanged<SR>(SRCollectionChangedWithInvoke);
con.Cache.DeregisterCollectionChanged<Folder>(FolderCollectionChangedWithInvoke);
con.Cache.DeregisterCollectionChanged<Task>(TaskCollectionChangedWithInvoke);
con.CachePopulated -= connection_CachePopulated;
foreach (VM vm in con.Cache.VMs)
{
ConsolePanel.closeVNCForSource(vm);
}
foreach (Host host in con.Cache.Hosts)
{
ConsolePanel.closeVNCForSource(host.ControlDomainZero());
foreach (VM vm in host.OtherControlDomains())
CvmConsolePanel.closeVNCForSource(vm);
}
con.EndConnect();
}
//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();
RequestRefreshTreeView();
}
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 {4} {5} (build {6}.{7})",
Helpers.GetName(master), Helpers.HostProductVersionText(master), Helpers.HostProductVersion(master),
master.BuildNumberRaw(), Messages.XENCENTER, Branding.PRODUCT_VERSION_TEXT,
Branding.XENCENTER_VERSION, Program.Version.Revision);
// Check the PRODUCT_BRAND
if (!Program.RunInAutomatedTestMode && !SameProductBrand(master))
{
connection.EndConnect();
Program.Invoke(Program.MainWindow, delegate
{
var title = string.Format(Messages.CONNECTION_REFUSED_TITLE, Helpers.GetName(master).Ellipsise(80));
new ActionBase(title, "", false, true, Messages.INCOMPATIBLE_PRODUCTS);
using (var dlog = new ConnectionRefusedDialog {ErrorMessage = Messages.INCOMPATIBLE_PRODUCTS, Url = ""})
dlog.ShowDialog(this);
});
return;
}
// 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
{
var msg = string.Format(Messages.GUI_OUT_OF_DATE, Helpers.GetName(master));
var url = "https://" + connection.Hostname;
var title = string.Format(Messages.CONNECTION_REFUSED_TITLE, Helpers.GetName(master).Ellipsise(80));
var error = string.Format("{0}\n{1}", msg, url);
new ActionBase(title, "", false, true, error);
using (var dlog = new ConnectionRefusedDialog {ErrorMessage = msg, Url = url})
dlog.ShowDialog(this);
});
return;
}
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);
if (Properties.Settings.Default.ShowHealthCheckEnrollmentReminder)
ThreadPool.QueueUserWorkItem(CheckHealthCheckEnrollment, connection);
ThreadPool.QueueUserWorkItem(HealthCheck.CheckForAnalysisResults, connection);
ThreadPool.QueueUserWorkItem(InformHealthCheckEnrollment, connection);
Updates.CheckServerPatches();
Updates.CheckServerVersion();
HealthCheck.SendMetadataToHealthCheck();
RequestRefreshTreeView();
}
private void CheckHealthCheckEnrollment(object connection)
{
if (HealthCheckOverviewLauncher != null && !HiddenFeatures.HealthCheckHidden)
HealthCheckOverviewLauncher.CheckHealthCheckEnrollment((IXenConnection) connection);
}
private void InformHealthCheckEnrollment(object connection)
{
Pool pool = Helpers.GetPoolOfOne((IXenConnection)connection);
if (pool == null)
return;
var newHealthCheckSSettings = pool.HealthCheckSettings();
new TransferHealthCheckSettingsAction(pool, newHealthCheckSSettings,
newHealthCheckSSettings.GetSecretyInfo(pool.Connection, HealthCheckSettings.UPLOAD_CREDENTIAL_USER_SECRET),
newHealthCheckSSettings.GetSecretyInfo(pool.Connection, HealthCheckSettings.UPLOAD_CREDENTIAL_PASSWORD_SECRET), true).RunAsync();
}
private static bool SameProductBrand(Host host)
{
var brand = host.ProductBrand();
return brand == Branding.PRODUCT_BRAND || brand == Branding.LEGACY_PRODUCT_BRAND || Branding.PRODUCT_BRAND == "[XenServer product]";
}
/// <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();
}
}
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":
UpdateHeader();
UpdateToolbars();
break;
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":
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":
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.SetBodgeStartupTime(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.GetBodgeStartupTime().Ticks)
vm.SetBodgeStartupTime(newTime); // only update if is newer than current bodge startup time
}
}
private void Connection_ConnectionResult(object sender, Network.ConnectionResultEventArgs e)
{
RequestRefreshTreeView();
}
private void Connection_ConnectionClosed(object sender, EventArgs e)
{
RequestRefreshTreeView();
gc();
}
// called whenever our connection with the Xen server fails (i.e., after we've successfully logged in)
private void Connection_ConnectionLost(object sender, EventArgs e)
{
if (Program.Exiting)
return;
Program.Invoke(this, () => CloseActiveWizards((IXenConnection)sender));
RequestRefreshTreeView();
gc();
}
private static void gc()
{
GC.Collect();
}
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();
}
/// <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);
}
void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private bool _menuShortcutsEnabled = true;
public bool MenuShortcutsEnabled
{
get { return _menuShortcutsEnabled; }
set
{
if (value != _menuShortcutsEnabled)
{
//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
_menuShortcutsEnabled = 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();
try
{
ToolStrip.SuspendLayout();
UpdateToolbarsCore();
MainMenuBar_MenuActivate(null, null);
}
finally
{
ToolStrip.ResumeLayout();
}
// 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 static int TOOLBAR_HEIGHT = 31;
/// <summary>
/// Updates the toolbar buttons.
/// </summary>
private void UpdateToolbarsCore()
{
// refresh the selection-manager
SelectionManager.RefreshSelection();
ToolStrip.Height = ToolbarsEnabled ? TOOLBAR_HEIGHT : 0;
ToolStrip.Enabled = ToolbarsEnabled;
ShowToolbarMenuItem.Checked = toolbarToolStripMenuItem.Checked = ToolbarsEnabled;
bool containerButtonsAvailable = startContainerToolStripButton.Enabled || stopContainerToolStripButton.Enabled ||
resumeContainerToolStripButton.Enabled || pauseContainerToolStripButton.Enabled || restartContainerToolStripButton.Enabled;
startContainerToolStripButton.Available = containerButtonsAvailable && startContainerToolStripButton.Enabled;
stopContainerToolStripButton.Available = containerButtonsAvailable && (stopContainerToolStripButton.Enabled || !startContainerToolStripButton.Available);
resumeContainerToolStripButton.Available = containerButtonsAvailable && resumeContainerToolStripButton.Enabled;
pauseContainerToolStripButton.Available = containerButtonsAvailable && (pauseContainerToolStripButton.Enabled || !resumeContainerToolStripButton.Available);
restartContainerToolStripButton.Available = containerButtonsAvailable;
powerOnHostToolStripButton.Available = powerOnHostToolStripButton.Enabled;
startVMToolStripButton.Available = startVMToolStripButton.Enabled;
shutDownToolStripButton.Available = shutDownToolStripButton.Enabled || (!startVMToolStripButton.Available && !powerOnHostToolStripButton.Available && !containerButtonsAvailable);
RebootToolbarButton.Available = RebootToolbarButton.Enabled || !containerButtonsAvailable;
resumeToolStripButton.Available = resumeToolStripButton.Enabled;
SuspendToolbarButton.Available = SuspendToolbarButton.Enabled || (!resumeToolStripButton.Available && !containerButtonsAvailable);
ForceRebootToolbarButton.Available = ((ForceVMRebootCommand)ForceRebootToolbarButton.Command).ShowOnMainToolBar;
ForceShutdownToolbarButton.Available = ((ForceVMShutDownCommand)ForceShutdownToolbarButton.Command).ShowOnMainToolBar;
}
private List<TabPage> GetNewTabPages()
{
IXenConnection selectionConnection = SelectionManager.Selection.GetConnectionOfFirstItem();
Pool selectionPool = selectionConnection == null ? null : Helpers.GetPool(selectionConnection);
// '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;
// 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 ha_upsell = Helpers.FeatureForbidden(SelectionManager.Selection.FirstAsXenObject, Host.RestrictHA) && (selectionPool != null && !selectionPool.ha_enabled);
bool wlb_upsell = Helpers.FeatureForbidden(SelectionManager.Selection.FirstAsXenObject, Host.RestrictWLB);
bool ad_upsell = Helpers.FeatureForbidden(SelectionManager.Selection.FirstAsXenObject, Host.RestrictAD);
bool is_connected = selectionConnection != null && selectionConnection.IsConnected;
bool multi = SelectionManager.Selection.Count > 1;
bool isPoolSelected = SelectionManager.Selection.FirstIs<Pool>();
bool isVMSelected = SelectionManager.Selection.FirstIs<VM>();
bool isHostSelected = SelectionManager.Selection.FirstIs<Host>();
bool isSRSelected = SelectionManager.Selection.FirstIs<SR>();
bool isVdiSelected = SelectionManager.Selection.FirstIs<VDI>();
bool isRealVMSelected = SelectionManager.Selection.FirstIsRealVM;
bool isTemplateSelected = SelectionManager.Selection.FirstIsTemplate;
bool isHostLive = SelectionManager.Selection.FirstIsLiveHost;
bool isDockerContainerSelected = SelectionManager.Selection.First is DockerContainer;
bool hasManyControlDomains = isHostSelected && ((Host)SelectionManager.Selection.First).HasManyControlDomains();
bool selectedTemplateHasProvisionXML = SelectionManager.Selection.FirstIsTemplate && ((VM)SelectionManager.Selection[0].XenObject).HasProvisionXML();
var newTabs = new List<TabPage>();
if (!SearchMode && show_home)
newTabs.Add(TabPageHome);
if (!multi && !SearchMode && (isVMSelected || (isHostSelected && (isHostLive || !is_connected)) ||
isPoolSelected || isSRSelected || isVdiSelected || isDockerContainerSelected))
newTabs.Add(TabPageGeneral);
if (!multi && !SearchMode && (isVMSelected || (isHostSelected && isHostLive) || isPoolSelected))
newTabs.Add(TabPageBallooning);
if (!multi && !SearchMode && (isRealVMSelected || (isTemplateSelected && !selectedTemplateHasProvisionXML)))
newTabs.Add(TabPageStorage);
if (!multi && !SearchMode && isSRSelected)
newTabs.Add(TabPageSR);
if (!multi && !SearchMode && ((isHostSelected && isHostLive) || isPoolSelected))
newTabs.Add(TabPagePhysicalStorage);
if (!multi && !SearchMode && (isVMSelected || (isHostSelected && isHostLive) || isPoolSelected))
newTabs.Add(TabPageNetwork);
if (!multi && !SearchMode && isHostSelected && isHostLive)
newTabs.Add(TabPageNICs);
if (!multi && !SearchMode && isDockerContainerSelected && !(SelectionManager.Selection.First as DockerContainer).Parent.IsWindows())
newTabs.Add(TabPageDockerProcess);
if (!multi && !SearchMode && isDockerContainerSelected)
newTabs.Add(TabPageDockerDetails);
bool isPoolOrLiveStandaloneHost = isPoolSelected || (isHostSelected && isHostLive && selectionPool == null);
if (!multi && !SearchMode && ((isHostSelected && isHostLive) || isPoolOrLiveStandaloneHost) && Helpers.ClearwaterSp1OrGreater(selectionConnection) && !Helpers.FeatureForbidden(selectionConnection, Host.RestrictGpu))
newTabs.Add(TabPageGPU);
if (!multi && !SearchMode && (isHostSelected && isHostLive && (((Host)SelectionManager.Selection.First).PUSBs.Count > 0)) && !Helpers.FeatureForbidden(selectionConnection, Host.RestrictUsbPassthrough))
newTabs.Add(TabPageUSB);
var consoleFeatures = new List<TabPageFeature>();
var otherFeatures = new List<TabPageFeature>();
if (SelectionManager.Selection.Count == 1 && !SearchMode)
GetFeatureTabPages(SelectionManager.Selection.FirstAsXenObject, out consoleFeatures, out otherFeatures);
foreach (var f in consoleFeatures)
newTabs.Add(f.TabPage);
if (consoleFeatures.Count == 0 && !multi && !SearchMode && (isRealVMSelected || (isHostSelected && isHostLive)))
newTabs.Add(TabPageConsole);
if (consoleFeatures.Count == 0 && !multi && !SearchMode && isHostLive && hasManyControlDomains)
newTabs.Add(TabPageCvmConsole);
if (!multi && !SearchMode && (isRealVMSelected || (isHostSelected && isHostLive)))
newTabs.Add(TabPagePeformance);
if (!multi && !SearchMode && isPoolSelected)
newTabs.Add(ha_upsell ? TabPageHAUpsell : TabPageHA);
if(!multi && !SearchMode && isRealVMSelected)
newTabs.Add(TabPageSnapshots);
//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)
&& !multi && !SearchMode && isPoolSelected)
newTabs.Add(TabPageWLB);
if (!multi && !SearchMode && (isPoolSelected || isHostSelected && isHostLive))
newTabs.Add(ad_upsell ? TabPageADUpsell : TabPageAD);
if (!multi && !SearchMode && isPoolOrLiveStandaloneHost && !Helpers.FeatureForbidden(SelectionManager.Selection.FirstAsXenObject, Host.RestrictPvsCache)
&& Helpers.PvsCacheCapability(selectionConnection))
newTabs.Add(TabPagePvs);
foreach (var f in otherFeatures)
newTabs.Add(f.TabPage);
newTabs.Add(TabPageSearch);
// N.B. Change NewTabs definition if you add more tabs here.
return newTabs;
}
private void GetFeatureTabPages(IXenObject xenObject, out List<TabPageFeature> consoleFeatures, out List<TabPageFeature> otherFeatures)
{
consoleFeatures = new List<TabPageFeature>();
otherFeatures = new List<TabPageFeature>();
var plugins = pluginManager.Plugins;
foreach (var p in plugins)
{
var features = p.Features;
foreach (var feature in features)
{
var f = feature as TabPageFeature;
if (f == null)
continue;
f.SelectedXenObject = xenObject;
if (!f.ShowTab)
continue;
if (f.IsConsoleReplacement)
{
f.SetUrl();
if (!f.IsError)
consoleFeatures.Add(f);
}
else
{
var page = GetLastSelectedPage(xenObject);
if (page != null && page.Tag == f)
f.SetUrl();
otherFeatures.Add(f);
}
}
}
}
private void ChangeToNewTabs()
{
var newTabs = GetNewTabPages();
var pageToSelect = GetLastSelectedPage(SelectionManager.Selection.First);
if (pageToSelect != null && !newTabs.Contains(pageToSelect))
pageToSelect = null;
TheTabControl.SuspendLayout();
IgnoreTabChanges = true;
try
{
foreach (TabPage page in TheTabControl.TabPages)
{
if (!newTabs.Contains(page))
TheTabControl.TabPages.Remove(page);
}
int m = 0; // Index into TheTabControl.TabPages
foreach (var newTab in newTabs)
{
var index = TheTabControl.TabPages.IndexOf(newTab);
if (index < 0)
TheTabControl.TabPages.Insert(m, newTab);
m++;
if (newTab == pageToSelect)
TheTabControl.SelectedTab = newTab;
}
if (pageToSelect == null)
TheTabControl.SelectedTab = TheTabControl.TabPages[0];
}
finally
{
IgnoreTabChanges = false;
TheTabControl.ResumeLayout();
SetLastSelectedPage(SelectionManager.Selection.First, TheTabControl.SelectedTab);
}
}
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 void pluginManager_PluginsChanged()
{
UpdateToolbars();
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 (Plugins.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());
}
}
private void MainMenuBar_MenuActivate(object sender, EventArgs e)
{
bool vm = SelectionManager.Selection.FirstIsRealVM && !((VM)SelectionManager.Selection.First).Locked;
exportSettingsToolStripMenuItem.Enabled = ConnectionsManager.XenConnectionsCopy.Count > 0;
MenuShortcutsEnabled = true;
startOnHostToolStripMenuItem.Available = startOnHostToolStripMenuItem.Enabled;
resumeOnToolStripMenuItem.Available = resumeOnToolStripMenuItem.Enabled;
relocateToolStripMenuItem.Available = relocateToolStripMenuItem.Enabled;
sendCtrlAltDelToolStripMenuItem.Enabled = (TheTabControl.SelectedTab == TabPageConsole) && vm && ((VM)SelectionManager.Selection.First).power_state == vm_power_state.Running;
IXenConnection conn = SelectionManager.Selection.GetConnectionOfAllItems();
if (SelectionManager.Selection.Count > 0 && (Helpers.GetMaster(conn) != null) && (Helpers.FalconOrGreater(conn)))
{
assignSnapshotScheduleToolStripMenuItem.Available = true;
VMSnapshotScheduleToolStripMenuItem.Available = true;
}
else /* hide VMSS */
{
assignSnapshotScheduleToolStripMenuItem.Available = false;
VMSnapshotScheduleToolStripMenuItem.Available = false;
}
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);
using (var 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");
}
private void TheTabControl_Deselected(object sender, TabControlEventArgs e)
{
TabPage t = e.TabPage;
if (t == null)
return;
BaseTabPage tabPage = t.Controls.OfType<BaseTabPage>().FirstOrDefault();
if (tabPage != null)
tabPage.PageHidden();
}
/// <param name="sender"></param>
/// <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 == TabPageConsole)
{
if (SelectionManager.Selection.FirstIsRealVM)
{
ConsolePanel.setCurrentSource((VM)SelectionManager.Selection.First);
UnpauseVNC(e != null && sender == TheTabControl);
}
else if (SelectionManager.Selection.FirstIs<Host>())
{
ConsolePanel.setCurrentSource((Host)SelectionManager.Selection.First);
UnpauseVNC(e != null && sender == TheTabControl);
}
ConsolePanel.UpdateRDPResolution();
}
else if (t == TabPageCvmConsole)
{
if (SelectionManager.Selection.FirstIs<Host>())
{
CvmConsolePanel.setCurrentSource((Host)SelectionManager.Selection.First);
UnpauseVNC(e != null && sender == TheTabControl);
}
}
else
{
ConsolePanel.PauseAllViews();
CvmConsolePanel.PauseAllViews();
// Start timer for closing the VNC connection after an interval (20 seconds)
// when the console tab is not selected
ConsolePanel.StartCloseVNCTimer(ConsolePanel.activeVNCView);
CvmConsolePanel.StartCloseVNCTimer(CvmConsolePanel.activeVNCView);
if (t == TabPageGeneral)
{
GeneralPage.XenObject = SelectionManager.Selection.FirstAsXenObject;
}
else if (t == TabPageBallooning)
{
BallooningPage.XenObject = SelectionManager.Selection.FirstAsXenObject;
}
else if (t == TabPageSR)
{
2015-10-27 19:11:53 +01:00
SrStoragePage.SR = SelectionManager.Selection.First as SR;
}
else if (t == TabPageNetwork)
{
NetworkPage.XenObject = SelectionManager.Selection.FirstAsXenObject;
}
else if (t == TabPageUSB)
{
UsbPage.XenObject = SelectionManager.Selection.FirstAsXenObject as Host;
}
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)
{
//We are in Objects View
GroupingTag gt = null;
if (SelectionManager.Selection.Count == 1)
{
gt = SelectionManager.Selection.First as GroupingTag
?? SelectionManager.Selection[0].GroupAncestor;
}
else
{
//If multiple items have been selected we count the number of the grouping tags in the selection
var selectedGroups = SelectionManager.Selection.Where(s => s.GroupingTag != null);
//if exactly one grouping tag has been selected we show the search view for that one tag, but only if all the other items in the selection belong to this group/tag
if (selectedGroups.Count() == 1)
{
var groupingTag = selectedGroups.First().GroupingTag;
if (SelectionManager.Selection.Where(s => s.GroupingTag == null).All(s => s.GroupAncestor == groupingTag))
gt = groupingTag;
else
gt = null;
}
else
{
gt = SelectionManager.Selection.GroupAncestor;
}
}
//if there has been a grouping tag determined above we use that
//if not we show the search view for the root node
if (gt != null)
{
SearchPage.Search = Search.SearchForNonVappGroup(gt.Grouping, gt.Parent, gt.Group);
}
else
{
SearchPage.Search = Search.SearchForNonVappGroup(rootNodeGrouping.Grouping, rootNodeGrouping.Parent, rootNodeGrouping.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
{
// Infrastructure View:
// If XenCenter node or a disconnected host is selected, show the default search
// Otherwise, find the top-level parent (= pool or standalone server) and show the search restricted to that
// In the case of multiselect, if all the selections are within one pool (or standalone server), then show that report.
// Otherwise show everything, as on the XenCenter node.
var connection = SelectionManager.Selection.GetConnectionOfAllItems(); // null for cross-pool selection
if (connection != null)
{
//If ShowJustHostInSearch is enabled and only one live host is selected, we show the search for the host only
if (Properties.Settings.Default.ShowJustHostInSearch && SelectionManager.Selection.Count == 1
&& SelectionManager.Selection.FirstIsLiveHost)
{
SearchPage.XenObject = SelectionManager.Selection.FirstAsXenObject;
}
else
{
var pool = Helpers.GetPool(connection);
SearchPage.XenObject = pool ?? (IXenObject)Helpers.GetMaster(connection); // pool or standalone server
}
}
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;
}
else if (t == TabPageDockerProcess)
{
DockerProcessPage.DockerContainer = SelectionManager.Selection.First as DockerContainer;
}
else if (t == TabPageDockerDetails)
{
DockerDetailsPage.DockerContainer = SelectionManager.Selection.First as DockerContainer;
}
else if (t == TabPagePvs)
{
PvsPage.Connection = SelectionManager.Selection.GetConnectionOfFirstItem();
}
}
if (t == TabPageSearch)
SearchPage.PanelShown();
else
SearchPage.PanelHidden();
if (t == TabPageDockerDetails)
DockerDetailsPage.ResumeRefresh();
else
DockerDetailsPage.PauseRefresh();
if (t == TabPageDockerProcess)
DockerProcessPage.ResumeRefresh();
else
DockerProcessPage.PauseRefresh();
if (t != null)
SetLastSelectedPage(SelectionManager.Selection.First, t);
UpdateTabePageFeatures();
}
private void UpdateTabePageFeatures()
{
var plugins = pluginManager.Plugins;
foreach (var p in plugins)
{
var features = p.Features;
foreach (var feature in features)
{
var f = feature as TabPageFeature;
if (f == null)
continue;
if (!f.ShowTab)
continue;
if (f.IsConsoleReplacement)
{
f.SetUrl();
continue;
}
var page = GetLastSelectedPage(f.SelectedXenObject);
if (page != null && page.Tag == f)
f.SetUrl();
}
}
}
private void UnpauseVNC(bool focus)
{
ConsolePanel.UnpauseActiveView();
CvmConsolePanel.UnpauseActiveView();
if (focus)
{
ConsolePanel.FocusActiveView();
CvmConsolePanel.FocusActiveView();
ConsolePanel.SwitchIfRequired();
CvmConsolePanel.SwitchIfRequired();
}
}
/// <summary>
/// The tabs that may be visible in the main GUI window. Used in SwitchToTab().
/// </summary>
public enum Tab
{
Home, General, Storage, Network, Console, CvmConsole, Performance, NICs, SR, DockerProcess, DockerDetails, USB, Search
}
public void SwitchToTab(Tab tab)
{
switch (tab)
{
case Tab.Home:
TheTabControl.SelectedTab = TabPageHome;
break;
case Tab.General:
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.CvmConsole:
TheTabControl.SelectedTab = TabPageCvmConsole;
break;
case Tab.Performance:
TheTabControl.SelectedTab = TabPagePeformance;
break;
case Tab.NICs:
TheTabControl.SelectedTab = TabPageNICs;
break;
case Tab.SR:
TheTabControl.SelectedTab = TabPageSR;
break;
case Tab.DockerProcess:
TheTabControl.SelectedTab = TabPageDockerProcess;
break;
case Tab.DockerDetails:
TheTabControl.SelectedTab = TabPageDockerDetails;
break;
case Tab.USB:
TheTabControl.SelectedTab = TabPageUSB;
break;
case Tab.Search:
TheTabControl.SelectedTab = TabPageSearch;
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)
{
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(
SystemIcons.Error,
string.Format(Messages.MESSAGEBOX_SAVE_CORRUPTED, Settings.GetUserConfigPath()),
Messages.MESSAGEBOX_SAVE_CORRUPTED_TITLE)))
{
dlg.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 Form ShowForm(Type type)
{
return 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 Form ShowForm(Type type, object[] args)
{
foreach (Form form in Application.OpenForms)
{
if (form.GetType() == type)
{
HelpersGUI.BringFormToFront(form);
return form;
}
}
Form newForm = (Form)Activator.CreateInstance(type, args);
newForm.Show(this);
return newForm;
}
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);
}
#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 == TabPageCvmConsole)
return "TabPageCvmConsole" + 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 == TabPageADUpsell)
return "TabPageADUpsell" + modelObj;
if (TheTabControl.SelectedTab == TabPageWLBUpsell)
return "TabPageWLBUpsell";
if (TheTabControl.SelectedTab == TabPageGPU)
return "TabPageGPU" + modelObj;
if (TheTabControl.SelectedTab == TabPageDockerProcess)
return "TabPageDockerProcess" + modelObj;
if (TheTabControl.SelectedTab == TabPageDockerDetails)
return "TabPageDockerDetails" + modelObj;
if (TheTabControl.SelectedTab == TabPagePvs)
return "TabPagePvs" + modelObj;
if (TheTabControl.SelectedTab == TabPageUSB)
return "TabPageUSB" + 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.FirstIs<Pool>())
return "Pool";
if (SelectionManager.Selection.FirstIs<Host>())
return "Server";
if (SelectionManager.Selection.FirstIs<VM>())
return "VM";
if (SelectionManager.Selection.FirstIs<SR>())
return "Storage";
}
if (TheTabControl.SelectedTab == TabPageNetwork)
{
if (SelectionManager.Selection.FirstIs<Host>())
return "Server";
if (SelectionManager.Selection.FirstIs<VM>())
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);
}
}
// record help usage
Properties.Settings.Default.HelpLastUsed = DateTime.UtcNow.ToString("u");
Settings.TrySaveSettings();
}
public void MainWindow_HelpRequested(object sender, HelpEventArgs hlpevent)
{
// CA-28064. MessageBox hack to kill the hlpevent it passes to MainWindows.
if (Program.MainWindow.ContainsFocus && MenuShortcutsEnabled)
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();
}
private 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();
}
private void InstallUpdate(string path)
{
if (WizardHelpers.IsValidFile(path))
{
var wizard = new PatchingWizard();
wizard.Show(this);
wizard.NextStep();
wizard.AddFile(path);
}
else
using (var popup = new ThreeButtonDialog(new ThreeButtonDialog.Details(
SystemIcons.Error, string.Format(Messages.UPDATES_WIZARD_NOTVALID_EXTENSION, Branding.Update), Messages.UPDATES)))
{
popup.ShowDialog();
}
}
#region XenSearch
private bool searchMode;
/// <summary>
/// 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.
/// </summary>
private bool SearchMode
{
get
{
return searchMode;
}
set
{
if (searchMode == value)
return;
searchMode = value;
navigationPane.InSearchMode = value;
UpdateToolbars();
}
}
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 (navigationPane.currentMode == NavigationPane.NavigationMode.Notifications)
return;
var licenseColor = Program.TitleBarForeColor;
var licenseText = string.Empty;
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 = xenObject.NameWithLocation();
TitleIcon.Image = Images.GetImage16For(xenObject);
licenseText = GetLicenseStatusText(xenObject, out licenseColor);
// 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;
}
LicenseStatusTitleLabel.Text = licenseText;
LicenseStatusTitleLabel.ForeColor = licenseColor;
SetTitleLabelMaxWidth();
}
private string GetLicenseStatusText(IXenObject xenObject, out Color foreColor)
{
foreColor = Program.TitleBarForeColor;
var pool = xenObject as Pool;
if (pool != null && pool.Connection != null && pool.Connection.IsConnected && pool.Connection.CacheIsPopulated)
{
if (pool.IsFreeLicenseOrExpired())
{
foreColor = Color.Red;
return Messages.MAINWINDOW_HEADER_UNLICENSED;
}
return string.Format(Messages.MAINWINDOW_HEADER_LICENSED_WITH, Helpers.GetFriendlyLicenseName(pool));
}
var host = xenObject as Host;
if (host != null && host.Connection != null && host.Connection.IsConnected && host.Connection.CacheIsPopulated)
{
if (host.IsFreeLicenseOrExpired())
{
foreColor = Color.Red;
return Messages.MAINWINDOW_HEADER_UNLICENSED;
}
return string.Format(Messages.MAINWINDOW_HEADER_LICENSED_WITH, Helpers.GetFriendlyLicenseName(host));
}
return string.Empty;
}
private void SetTitleLabelMaxWidth()
{
TitleLabel.MaximumSize = new Size(tableLayoutPanel1.Width - loggedInLabel1.Width - LicenseStatusTitleLabel.Width - 6, TitleLabel.Height);
}
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;
}
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)
{
switch (submodeItem.SubMode)
{
case NotificationsSubMode.Alerts:
if (updatesPage.Visible)
updatesPage.HidePage();
if (eventsPage.Visible)
eventsPage.HidePage();
alertPage.ShowPage();
break;
case NotificationsSubMode.Updates:
if (alertPage.Visible)
alertPage.HidePage();
if (eventsPage.Visible)
eventsPage.HidePage();
updatesPage.ShowPage();
break;
case NotificationsSubMode.Events:
if (alertPage.Visible)
alertPage.HidePage();
if (updatesPage.Visible)
updatesPage.HidePage();
eventsPage.ShowPage();
break;
}
TheTabControl.Visible = false;
loggedInLabel1.Connection = null;
TitleLabel.Text = submodeItem.Text;
TitleIcon.Image = submodeItem.Image;
}
private void navigationPane_NavigationModeChanged(NavigationPane.NavigationMode mode)
{
if (mode == NavigationPane.NavigationMode.Notifications)
{
LicenseStatusTitleLabel.Text = string.Empty;
TheTabControl.Visible = false;
}
else
{
bool tabControlWasVisible = TheTabControl.Visible;
TheTabControl.Visible = true;
if (alertPage.Visible)
alertPage.HidePage();
if (updatesPage.Visible)
updatesPage.HidePage();
if (eventsPage.Visible)
eventsPage.HidePage();
// 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()
{
// This is required to update search results when things change.
if (TheTabControl.SelectedTab == TabPageGeneral)
GeneralPage.BuildList();
else if (TheTabControl.SelectedTab == TabPageSearch)
SearchPage.BuildList();
UpdateHeader();
UpdateToolbars();
}
#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);
DialogResult dialogResult;
using (var dialog = String.IsNullOrEmpty(helpName)
? new ThreeButtonDialog(details, buttons)
: new ThreeButtonDialog(details, helpName, buttons))
{
dialogResult = dialog.ShowDialog(parent ?? Program.MainWindow);
}
if (dialogResult != 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;
}
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(
SystemIcons.Warning,
Messages.DISCONNECTED_BEFORE_ACTION_STARTED,
Messages.XENCENTER)))
{
dlg.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.
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);
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(SystemIcons.Error, Messages.ERRO_IMPORTING_SERVER_LIST, Messages.XENCENTER)))
{
dlg.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();
}
FormWindowState lastState = FormWindowState.Normal;
private void MainWindow_Resize(object sender, EventArgs e)
{
TabPage t = TheTabControl.SelectedTab;
if (t == TabPageConsole)
{
if (WindowState != lastState && WindowState != FormWindowState.Minimized)
{
lastState = WindowState;
ConsolePanel.UpdateRDPResolution();
}
mainWindowResized = true;
}
SetSplitterDistance();
SetTitleLabelMaxWidth();
}
private void SetSplitterDistance()
{
//CA-71697: chosen min size so the tab contents are visible
int chosenPanel2MinSize = MinimumSize.Width * 3 / 5;
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;
}
private void MainWindow_ResizeEnd(object sender, EventArgs e)
{
TabPage t = TheTabControl.SelectedTab;
if (t == TabPageConsole)
{
if (mainWindowResized)
ConsolePanel.UpdateRDPResolution();
mainWindowResized = false;
}
}
private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
{
TabPage t = TheTabControl.SelectedTab;
if (t == TabPageConsole)
ConsolePanel.UpdateRDPResolution();
SetTitleLabelMaxWidth();
}
}
}