xenadmin/XenAdmin/MainWindow.cs
mcintyre94 dd42394a25 CP-16922: Implement GUI for enabling PVS read caching (#1179)
* CP-16922: UI changes to PvsPage to replace the single configure button with a separate enable and disable one, per the design doc

* CP-16922: Intermediate checkin - command + dialog implemented, but PVS page has some layout problems.

* CP-16922:  Implement GUI for enabling PVS read caching and marking/unmarking VMs for prepopulation

Fix the broken layout by reverting MainWindow.resx (which shouldn't have changed) back to the previous commit. Also removed duplicate properties button code.

*  CP-16922: PVS Page changes, to properly display all VMs (not just ones with proxy), only populating proxy columns if there is a proxy set.

* CP-16922: Use a CommandButton for the enable button, connected to the same command as the VM main menu button, and using the table selection as its selection manager.

* CP-16922: Delete the old enable button click behaviour (because it now has a proper command), was causing the dialog to show up twice

* CP-16922: Properly list VMs by hiding those that should be hidden (in addition to non-VMs, also hide the __gui__ prefix and others covered by Show function). Also fixes a bug in NewVMGroupVMsPage discovered when fixing this - it would show VMs that should be hidden if you used the search bar.


* CP-16922: Use ButtonText on the Command to set the text on the PVS enable button

* CP-16922: Add the disable PVS read-caching command, and its associated proxy destroy action. Both based off enable equivalents (main difference is no dialog for now). Not used yet but will be in main window menu + disable button

* CP-16922: Add disable PVS read-caching to the main window menu (under enable), with the disable command attached to it


* CP-16922: Add support for disabling PVS read-caching for selected hosts via the PVS tab page by converting the disable button into a CommandButton in the same way as the enable button, using its own command + selection manager (because Selection Managers only bind to one control)

* CP-16922: Design - adding the "read caching enabled" column from the design, so that we display both whether caching is enabled and whether the VM is currently cached. If it's disabled all other values are missing.


* CP-16922: Add the PVS site column per the design to show the site a VM's PVS Proxy is on if it has a proxy


* CP-16922: Add an event handler for VMs changing so that newly provisioned VMs immediately show up in the PVS tab page


* CP-16922: Support for properly remembering the selection when the VM list is refreshed. A new method returns the list of VMs for the selected rows, and we can then check if the VM for the row being added is in this list. If there was no previous selection then we still select the first row by default.


* CP-16922: Remove prepopulate, which is being removed from the design. The next XAPI version will remove it, so just pass False as prepopulate for now, and remove the option from the dialog and PVS tab page.


* CP-16922: Improve UI appearance for a missing Storage Repository. Per the design, if there is no SR show the message None (memory only) instead of an empty cell

* CP-16922: Improved sorting behaviour. All columns (except VM name, which always uses it) use the VM tag as a sorting fallback (since they all have duplicates). New default sort which sorts by caching enabled first (yes before no), and then VM tag as a tiebreaker. Support for remembering the current sort on refresh, ie when a value changes.

* CP-16922: Refactoring to use a VM extension method to get the PVSProxy (or null if none exists) for a VM. This was used in the PVS Page + commands which are now much simpler.


* CP-16922: Move where the SelectionChanged event handler is set when refreshing the PVS table, so that the command buttons are updated correctly.

* CP-16922: Improvements to PVS read-caching dialog. Set accept/cancel buttons properly + adjust tab values, so focus acts as expected


* CP-16922: Remove the images from the menu for Enable/Disable PVS read-caching, most commands in the menu don't have one

* CP-16922: Sorting out the strings for the actions + ParallelAction calls, and moving them into the resx. Also reverting wrong MainWindow.resx changes.

* CAR-2184: XAPI changes. I've made the minimal possible changes to PvsPage to ensure it compiles, but it's had a lot of feature work (CP-16922) that supersedes this, these small changes should keep that merge painless.


* CAR-2184 XAPI: Reverse the unrelated changes, only keep the PVS ones.

* CP-16922: Merge latest XAPI changes, notably changes to Pvs_site.create

* CP-16922: Change tab show/hide logic for PVS tab, show it even if there are no PVS sites.

* CP-16922: Implement GUI for enabling PVS read caching

Design changes - remove the storage repo column and replace it with status, which is populated with a friendly version of the pvs_proxy_status enum. Also populate site list using site.name_label

Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com>

*  CP-16922: Implement GUI for enabling PVS read caching

Adjusting the CanExecute of Enable PVS read-caching command to require at least one PVS site to be created. Since the PVS tab can show up without any sites, we need this because we can't create a proxy without a site.

* CP-16922: Code review changes.

Tidied up messages, and ensured actions run async properly, and without exiting before their task.

* CP-16922: Code review changes.

Update dialog resx, wider combobox on dialog, update action descriptions before setting RelatedTask, add friendly message body for PVS error notifications.
2016-09-22 15:08:15 +01:00

3301 lines
138 KiB
C#

/* Copyright (c) Citrix Systems Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using XenAdmin.Actions.GUIActions;
using XenAdmin.Controls.MainWindowControls;
using XenAdmin.Wizards.ImportWizard;
using XenAPI;
using XenAdmin.Actions;
using XenAdmin.Alerts;
using XenAdmin.Commands;
using XenAdmin.Controls;
using XenAdmin.Core;
using XenAdmin.Dialogs;
using XenAdmin.Model;
using XenAdmin.Network;
using XenAdmin.TabPages;
using XenAdmin.XenSearch;
using XenAdmin.Wizards.PatchingWizard;
using XenAdmin.Plugins;
using System.Linq;
namespace XenAdmin
{
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
[ComVisibleAttribute(true)]
public partial class MainWindow : Form, ISynchronizeInvoke, IMainWindow
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// A mapping between objects in the tree and the associated selected tab.
/// </summary>
private Dictionary<object, TabPage> selectedTabs = new Dictionary<object, TabPage>();
/// <summary>
/// The selected tab for the overview node.
/// </summary>
private TabPage selectedOverviewTab = null;
internal readonly PerformancePage PerformancePage = new PerformancePage();
internal readonly GeneralTabPage GeneralPage = new GeneralTabPage();
internal readonly BallooningPage BallooningPage = new BallooningPage();
internal readonly BallooningUpsellPage BallooningUpsellPage = new BallooningUpsellPage();
internal readonly ConsolePanel ConsolePanel = new ConsolePanel();
internal readonly 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();
private ActionBase statusBarAction = null;
public ActionBase StatusBarAction { get { return statusBarAction; } }
private bool IgnoreTabChanges = false;
private bool ToolbarsEnabled;
private readonly Dictionary<IXenConnection, IList<Form>> activePoolWizards = new Dictionary<IXenConnection, IList<Form>>();
private readonly Dictionary<IXenObject, Form> activeXenModelObjectWizards = new Dictionary<IXenObject, Form>();
/// <summary>
/// The arguments passed in on the command line.
/// </summary>
private string[] CommandLineParam = null;
private ArgType CommandLineArgType = ArgType.None;
private static readonly System.Windows.Forms.Timer CheckForUpdatesTimer = new System.Windows.Forms.Timer();
private readonly PluginManager pluginManager;
private readonly ContextMenuBuilder contextMenuBuilder;
private readonly LicenseManagerLauncher licenseManagerLauncher;
private readonly LicenseTimer licenseTimer;
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;
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;
#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);
AddTabContents(VMStoragePage, TabPageStorage);
AddTabContents(SrStoragePage, TabPageSR);
AddTabContents(NICPage, TabPageNICs);
AddTabContents(PerformancePage, TabPagePeformance);
AddTabContents(GeneralPage, TabPageGeneral);
AddTabContents(BallooningPage, TabPageBallooning);
AddTabContents(BallooningUpsellPage, TabPageBallooningUpsell);
AddTabContents(ConsolePanel, TabPageConsole);
AddTabContents(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);
#endregion
TheTabControl.SelectedIndexChanged += TheTabControl_SelectedIndexChanged;
navigationPane.DragDropCommandActivated += navigationPane_DragDropCommandActivated;
PoolCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged<Pool>);
MessageCollectionChangedWithInvoke = Program.ProgramInvokeHandler(MessageCollectionChanged);
HostCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged<Host>);
VMCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged<VM>);
SRCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged<SR>);
FolderCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged<Folder>);
TaskCollectionChangedWithInvoke = Program.ProgramInvokeHandler(MeddlingActionManager.TaskCollectionChanged);
ConnectionsManager.History.CollectionChanged += History_CollectionChanged;
CommandLineArgType = argType;
CommandLineParam = args;
pluginManager = new PluginManager();
pluginManager.PluginsChanged += pluginManager_PluginsChanged;
pluginManager.LoadPlugins();
contextMenuBuilder = new ContextMenuBuilder(pluginManager, this);
eventsPage.GoToXenObjectRequested += eventsPage_GoToXenObjectRequested;
SearchPage.SearchChanged += SearchPanel_SearchChanged;
Alert.RegisterAlertCollectionChanged(XenCenterAlerts_CollectionChanged);
Updates.RegisterCollectionChanged(Updates_CollectionChanged);
FormFontFixer.Fix(this);
Folders.InitFolders();
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.FirstIsHost)
ConsolePanel.setCurrentSource((Host)SelectionManager.Selection.First);
UnpauseVNC(sender == TheTabControl);
}
}
private void SetMenuItemStartIndexes()
{
foreach (ToolStripMenuItem menu in MainMenuBar.Items)
{
foreach (ToolStripItem item in menu.DropDownItems)
{
if (item != null && item.Text == "PluginItemsPlaceHolder")
{
pluginMenuItemStartIndexes.Add(menu, menu.DropDownItems.IndexOf(item));
menu.DropDownItems.Remove(item);
break;
}
}
}
}
internal SelectionBroadcaster SelectionManager
{
get
{
return navigationPane.SelectionManager;
}
}
internal ContextMenuBuilder ContextMenuBuilder
{
get
{
return contextMenuBuilder;
}
}
protected override void OnLoad(EventArgs e)
{
Program.AssertOnEventThread();
History.EnableHistoryButtons();
History.NewHistoryItem(new XenModelObjectHistoryItem(null, TabPageHome));
/*
* Resume window size and location
*/
try
{
// Bring in previous version user setting for the first time.
if (Properties.Settings.Default.DoUpgrade)
{
Properties.Settings.Default.Upgrade();
Properties.Settings.Default.DoUpgrade = false;
XenAdmin.Settings.TrySaveSettings();
}
Point savedLocation = Properties.Settings.Default.WindowLocation;
Size savedSize = Properties.Settings.Default.WindowSize;
if (HelpersGUI.WindowIsOnScreen(savedLocation, savedSize))
{
this.Location = savedLocation;
this.Size = savedSize;
}
}
catch
{
}
// Using the Load event ensures that the handle has been
// created:
base.OnLoad(e);
NewTabs[0] = TabPageHome;
NewTabCount = 1;
ChangeToNewTabs();
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
TheTabControl.Visible = true;
alertPage.Visible = updatesPage.Visible = eventsPage.Visible = false;
navigationPane.FocusTreeView();
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
Clip.RegisterClipboardViewer();
}
protected override void WndProc(ref System.Windows.Forms.Message e)
{
//System.Console.WriteLine(Win32.GetWindowsMessageName(e.Msg));
switch (e.Msg)
{
case Win32.WM_CHANGECBCHAIN: // Clipboard chain has changed.
Clip.ProcessWMChangeCBChain(e);
break;
case Win32.WM_DRAWCLIPBOARD: // Content of clipboard has changed.
Clip.ProcessWMDrawClipboard(e);
break;
case Win32.WM_DESTROY:
Clip.UnregisterClipboardViewer();
base.WndProc(ref e);
break;
default:
base.WndProc(ref e);
break;
}
}
private void AddTabContents(Control contents, TabPage TabPage)
{
contents.Location = new Point(0, 0);
contents.Size = TabPage.Size;
contents.Anchor = AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Right;
TabPage.Controls.Add(contents);
}
void History_CollectionChanged(object sender, CollectionChangeEventArgs e)
{
if (Program.Exiting)
return;
Program.BeginInvoke(Program.MainWindow, () =>
{
ActionBase action = (ActionBase)e.Element;
if (action == null)
return;
switch (e.Action)
{
case CollectionChangeAction.Add:
{
var meddlingAction = action as MeddlingAction;
if (meddlingAction == null)
{
SetStatusBar(null, null);
if (statusBarAction != null)
{
statusBarAction.Changed -= actionChanged;
statusBarAction.Completed -= actionChanged;
}
statusBarAction = action;
}
action.Changed += actionChanged;
action.Completed += actionChanged;
actionChanged(action);
break;
}
case CollectionChangeAction.Remove:
{
action.Changed -= actionChanged;
action.Completed -= actionChanged;
int errors = ConnectionsManager.History.Count(a => a.IsCompleted && !a.Succeeded);
navigationPane.UpdateNotificationsButton(NotificationsSubMode.Events, errors);
if (eventsPage.Visible)
{
TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Events, errors);
TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Events, errors);
}
break;
}
}
});
}
void actionChanged(ActionBase action)
{
if (Program.Exiting)
return;
Program.Invoke(this, () => actionChanged_(action));
}
void actionChanged_(ActionBase action)
{
var meddlingAction = action as MeddlingAction;
if (meddlingAction == null)
statusProgressBar.Visible = action.ShowProgress && !action.IsCompleted;
// Be defensive against CA-8517.
if (action.PercentComplete < 0 || action.PercentComplete > 100)
{
log.ErrorFormat("PercentComplete is erroneously {0}", action.PercentComplete);
}
else if (meddlingAction == null)
{
statusProgressBar.Value = action.PercentComplete;
}
// Don't show cancelled exception
if (action.Exception != null && !(action.Exception is CancelledException))
{
if (meddlingAction == null)
SetStatusBar(Properties.Resources._000_error_h32bit_16, action.Exception.Message);
}
else if (meddlingAction == null)
{
SetStatusBar(null, action.IsCompleted
? null
: !string.IsNullOrEmpty(action.Description)
? action.Description
: !string.IsNullOrEmpty(action.Title)
? action.Title
: null);
}
int errors = ConnectionsManager.History.Count(a => a.IsCompleted && !a.Succeeded && !((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);
}
}
public void SetStatusBar(Image image, string message)
{
if ((statusLabel.Text != null && statusLabel.Text.Equals(message))
|| (statusLabel.Image != null && statusLabel.Equals(image)))
return;
statusLabel.Image = image;
statusLabel.Text = Helpers.FirstLine(message);
}
public void SetProgressBar(bool visible, int progress)
{
statusProgressBar.Visible = visible;
statusProgressBar.Value = progress;
}
private void MainWindow_Shown(object sender, EventArgs e)
{
MainMenuBar.Location = new Point(0, 0);
if (ToolStrip.Renderer is ToolStripProfessionalRenderer)
{
((ToolStripProfessionalRenderer)ToolStrip.Renderer).RoundedEdges = false;
}
ConnectionsManager.XenConnections.CollectionChanged += XenConnection_CollectionChanged;
try
{
Settings.RestoreSession();
new TransferProxySettingsAction((HTTPHelper.ProxyStyle)Properties.Settings.Default.ProxySetting,
Properties.Settings.Default.ProxyAddress,
Properties.Settings.Default.ProxyPort,
Properties.Settings.Default.ConnectionTimeout,
Properties.Settings.Default.BypassProxyForLocal,
true).RunAsync();
}
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; // Application.Exit() does not exit the current method.
}
ToolbarsEnabled = Properties.Settings.Default.ToolbarsEnabled;
RequestRefreshTreeView();
UpdateToolbars();
// 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);
}
}
RequestRefreshTreeView();
ThreadPool.QueueUserWorkItem((WaitCallback)delegate(object o)
{
// Sleep a short time before closing the splash
Thread.Sleep(500);
Program.Invoke(Program.MainWindow, Program.CloseSplash);
});
if (!Program.RunInAutomatedTestMode && !Helpers.CommonCriteriaCertificationRelease)
{
if (!Properties.Settings.Default.SeenAllowUpdatesDialog)
new AllowUpdatesDialog(pluginManager).ShowDialog(this);
// start checkforupdates thread
CheckForUpdatesTimer.Interval = 1000 * 60 * 60 * 24; // 24 hours
CheckForUpdatesTimer.Tick += CheckForUpdatesTimer_Tick;
CheckForUpdatesTimer.Start();
Updates.CheckForUpdates(false);
}
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 Cache_Changed(object sender, EventArgs e)
{
RequestRefreshTreeView();
}
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:
System.Diagnostics.Trace.Assert(false);
break;
}
Launched = true;
}
// Manages UI and network updates whenever hosts are added and removed
void XenConnection_CollectionChanged(object sender, CollectionChangeEventArgs e)
{
if (Program.Exiting)
return;
//Program.AssertOnEventThread();
Program.BeginInvoke(Program.MainWindow, () => XenConnectionCollectionChanged(e));
}
private readonly CollectionChangeEventHandler PoolCollectionChangedWithInvoke = null;
private readonly CollectionChangeEventHandler MessageCollectionChangedWithInvoke = null;
private readonly CollectionChangeEventHandler HostCollectionChangedWithInvoke = null;
private readonly CollectionChangeEventHandler VMCollectionChangedWithInvoke = null;
private readonly CollectionChangeEventHandler SRCollectionChangedWithInvoke = null;
private readonly CollectionChangeEventHandler FolderCollectionChangedWithInvoke = null;
private readonly CollectionChangeEventHandler TaskCollectionChangedWithInvoke = null;
private void XenConnectionCollectionChanged(CollectionChangeEventArgs e)
{
try
{
IXenConnection connection = (IXenConnection)e.Element;
if (connection == null)
return;
navigationPane.XenConnectionCollectionChanged(e);
if (e.Action == CollectionChangeAction.Add)
{
connection.ClearingCache += connection_ClearingCache;
connection.ConnectionResult += Connection_ConnectionResult;
connection.ConnectionLost += Connection_ConnectionLost;
connection.ConnectionClosed += Connection_ConnectionClosed;
connection.ConnectionReconnecting += connection_ConnectionReconnecting;
connection.XenObjectsUpdated += Connection_XenObjectsUpdated;
connection.Cache.RegisterCollectionChanged<XenAPI.Message>(MessageCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged<Pool>(PoolCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged<Host>(HostCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged<VM>(VMCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged<SR>(SRCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged<Folder>(FolderCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged<Task>(TaskCollectionChangedWithInvoke);
connection.CachePopulated += connection_CachePopulated;
}
else if (e.Action == CollectionChangeAction.Remove)
{
connection.ClearingCache -= connection_ClearingCache;
connection.ConnectionResult -= Connection_ConnectionResult;
connection.ConnectionLost -= Connection_ConnectionLost;
connection.ConnectionClosed -= Connection_ConnectionClosed;
connection.ConnectionReconnecting -= connection_ConnectionReconnecting;
connection.XenObjectsUpdated -= Connection_XenObjectsUpdated;
connection.Cache.DeregisterCollectionChanged<XenAPI.Message>(MessageCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged<Pool>(PoolCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged<Host>(HostCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged<VM>(VMCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged<SR>(SRCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged<Folder>(FolderCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged<Task>(TaskCollectionChangedWithInvoke);
connection.CachePopulated -= connection_CachePopulated;
foreach (VM vm in connection.Cache.VMs)
{
ConsolePanel.closeVNCForSource(vm);
}
foreach (Host host in connection.Cache.Hosts)
{
ConsolePanel.closeVNCForSource(host.ControlDomainZero);
foreach (VM vm in host.OtherControlDomains)
CvmConsolePanel.closeVNCForSource(vm);
}
connection.EndConnect();
RequestRefreshTreeView();
//CA-41228 refresh submenu items when there are no connections
SelectionManager.RefreshSelection();
}
}
catch (Exception exn)
{
log.Error(exn, exn);
// Can't do any more about this.
}
}
/// <summary>
/// Closes any wizards for this connection. Must be done before we clear the cache so that per-VM wizards are closed.
/// In many cases this is already covered (e.g. if the user explicitly disconnects). This method ensures we also
/// do it when we unexpectedly lose the connection.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void connection_ClearingCache(object sender, EventArgs e)
{
IXenConnection connection = (IXenConnection)sender;
CloseActiveWizards(connection);
Alert.RemoveAlert(alert => alert.Connection != null && alert.Connection.Equals(connection));
Updates.CheckServerPatches();
Updates.CheckServerVersion();
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),
Helpers.HostBuildNumber(master), 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()
{
string msg = string.Format(Messages.INCOMPATIBLE_PRODUCTS, Helpers.GetName(master));
string url = "";
using (var dlog = new ConnectionRefusedDialog())
{
dlog.ErrorMessage = msg;
dlog.Url = "";
dlog.ShowDialog(this);
}
new ActionBase(Messages.CONNECTION_REFUSED_TITLE,
string.Format("{0}\n{1}", msg, url), false,
true, Messages.CONNECTION_REFUSED);
});
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()
{
string msg = string.Format(Messages.GUI_OUT_OF_DATE, Helpers.GetName(master));
msg = msg + Messages.GUI_OUT_OF_DATE_MORE;
string url = "https://" + connection.Hostname;
using (var dlog = new ConnectionRefusedDialog())
{
dlog.ErrorMessage = msg;
dlog.Url = url;
dlog.ShowDialog(this);
}
new ActionBase(Messages.CONNECTION_REFUSED_TITLE,
string.Format("{0}\n{1}", msg, url), false,
true, Messages.CONNECTION_REFUSED);
});
return;
}
else if (server_max > current_version)
{
Alert.AddAlert(new GuiOldAlert());
}
LoadTasksAsMeddlingActions(connection);
}
//
// Every time we connect, make sure any host with other_config[maintenance_mode] == true
// is disabled.
//
CheckMaintenanceMode(connection);
if (HelpersGUI.iSCSIisUsed())
HelpersGUI.PerformIQNCheck();
if(licenseTimer != null)
licenseTimer.CheckActiveServerLicense(connection, false);
if (Properties.Settings.Default.ShowHealthCheckEnrollmentReminder)
ThreadPool.QueueUserWorkItem(CheckHealthCheckEnrollment, connection);
ThreadPool.QueueUserWorkItem(HealthCheck.CheckForAnalysisResults, connection);
ThreadPool.QueueUserWorkItem(InformHealthCheckEnrollment, connection);
Updates.CheckServerPatches();
Updates.CheckServerVersion();
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();
}
public static bool SameProductBrand(Host host)
{
return host.ProductBrand == Branding.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();
Program.Invoke(this, UpdateToolbars);
}
}
void MessageCollectionChanged(object sender, CollectionChangeEventArgs e)
{
Program.AssertOnEventThread();
XenAPI.Message m = (XenAPI.Message)e.Element;
if (e.Action == CollectionChangeAction.Add)
{
if (!m.ShowOnGraphs && !m.IsSquelched)
Alert.AddAlert(MessageAlert.ParseMessage(m));
}
else if (e.Action == CollectionChangeAction.Remove)
{
if (!m.ShowOnGraphs)
MessageAlert.RemoveAlert(m);
}
}
void CollectionChanged<T>(object sender, CollectionChangeEventArgs e) where T : XenObject<T>
{
Program.AssertOnEventThread();
T o = (T)e.Element;
if (e.Action == CollectionChangeAction.Add)
{
if (o is Pool)
((Pool)e.Element).PropertyChanged += Pool_PropertyChanged;
else if (o is Host)
((Host)e.Element).PropertyChanged += Host_PropertyChanged;
else if (o is VM)
((VM)e.Element).PropertyChanged += VM_PropertyChanged;
else
o.PropertyChanged += o_PropertyChanged;
}
else if (e.Action == CollectionChangeAction.Remove)
{
if (o is Pool)
((Pool)e.Element).PropertyChanged -= Pool_PropertyChanged;
else if (o is Host)
((Host)e.Element).PropertyChanged -= Host_PropertyChanged;
else if (o is VM)
((VM)e.Element).PropertyChanged -= VM_PropertyChanged;
else
o.PropertyChanged -= o_PropertyChanged;
if (o is VM)
{
VM vm = (VM)e.Element;
ConsolePanel.closeVNCForSource(vm);
CloseActiveWizards(vm);
}
selectedTabs.Remove(o);
pluginManager.DisposeURLs(o);
}
}
private void Pool_PropertyChanged(object obj, PropertyChangedEventArgs e)
{
Pool pool = (Pool)obj;
switch (e.PropertyName)
{
case "other_config":
// other_config may contain HideFromXenCenter
UpdateToolbars();
// other_config contains which patches to ignore
Updates.CheckServerPatches();
Updates.CheckServerVersion();
break;
case "name_label":
pool.Connection.FriendlyName = Helpers.GetName(pool);
break;
}
}
private void Host_PropertyChanged(object obj, PropertyChangedEventArgs e)
{
Host host = (Host)obj;
switch (e.PropertyName)
{
case "allowed_operations":
case "enabled":
// We want to ensure that a host is disabled if it is in maintenance mode, by starting a new DisableHostAction if necessary (CheckMaintenanceMode)
if (host.enabled && host.MaintenanceMode)
{
// This is an invalid state: the host is enabled but still "in maintenance mode";
// But maybe MaintenanceMode hasn't been updated yet, because host.enabled being processed before host.other_config (CA-75625);
// We'll check it again after the cache update operation is complete, in Connection_XenObjectsUpdated
hostsInInvalidState.Add(host);
}
UpdateToolbars();
break;
case "edition":
case "license_server":
case "license_params":
case "other_config":
// other_config may contain HideFromXenCenter
UpdateToolbars();
break;
case "name_label":
//check whether it's a standalone host
if(Helpers.GetPool(host.Connection) == null)
host.Connection.FriendlyName = Helpers.GetName(host);
break;
}
}
private void VM_PropertyChanged(object obj, PropertyChangedEventArgs e)
{
VM vm = (VM)obj;
switch (e.PropertyName)
{
case "allowed_operations":
case "is_a_template":
case "resident_on":
UpdateToolbars();
break;
case "power_state":
UpdateToolbars();
// Make all vms have the correct start times
UpdateBodgedTime(vm, e.PropertyName);
break;
case "other_config":
// other_config may contain HideFromXenCenter
UpdateToolbars();
// Make all vms have the correct start times
UpdateBodgedTime(vm, e.PropertyName);
break;
}
}
void o_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "allowed_operations":
case "power_state":
case "is_a_template":
case "enabled":
UpdateToolbars();
break;
case "other_config":
// other_config may contain HideFromXenCenter
UpdateToolbars();
break;
}
}
// Update bodged startup time if the powerstate goes to running (vm started from halted), otherconfig last shutdown changed (vm rebooted) or start time changed (occurs a few seconds after start)
private void UpdateBodgedTime(VM vm, string p)
{
if (vm == null)
return;
if (p == "power_state")
{
vm.BodgeStartupTime = DateTime.UtcNow; // always newer than current bodge startup time
}
else if (p == "other_config" && vm.other_config.ContainsKey("last_shutdown_time"))
{
DateTime newTime = vm.LastShutdownTime;
if (newTime != DateTime.MinValue && newTime.Ticks > vm.BodgeStartupTime.Ticks)
vm.BodgeStartupTime = newTime; // only update if is newer than current bodge startup time
}
}
void Connection_ConnectionResult(object sender, Network.ConnectionResultEventArgs e)
{
RequestRefreshTreeView();
Program.Invoke(this, (EventHandler<ConnectionResultEventArgs>)Connection_ConnectionResult_, sender, e);
}
private void Connection_ConnectionResult_(object sender, Network.ConnectionResultEventArgs e)
{
Program.AssertOnEventThread();
try
{
UpdateToolbars();
}
catch (Exception exn)
{
log.Error(exn, exn);
// Can do nothing more about this.
}
}
void Connection_ConnectionClosed(object sender, EventArgs e)
{
RequestRefreshTreeView();
Program.Invoke(this, (EventHandler<Network.ConnectionResultEventArgs>)Connection_ConnectionClosed_, sender, e);
gc();
}
private void Connection_ConnectionClosed_(object sender, EventArgs e)
{
Program.AssertOnEventThread();
try
{
UpdateToolbars();
}
catch (Exception exn)
{
log.Error(exn, exn);
// Nothing more we can do with this.
}
}
// called whenever our connection with the Xen server fails (i.e., after we've successfully logged in)
void Connection_ConnectionLost(object sender, EventArgs e)
{
if (Program.Exiting)
return;
Program.Invoke(this, (EventHandler)Connection_ConnectionLost_, sender, e);
RequestRefreshTreeView();
gc();
}
private void Connection_ConnectionLost_(object sender, EventArgs e)
{
Program.AssertOnEventThread();
try
{
IXenConnection connection = (IXenConnection)sender;
CloseActiveWizards(connection);
UpdateToolbars();
}
catch (Exception exn)
{
log.Error(exn, exn);
// Can do nothing about this.
}
}
private static void gc()
{
//log_gc("Before"); // Don't log this, CA-159791
GC.Collect();
//log_gc("After");
}
private static void log_gc(string when)
{
log.DebugFormat("{0} GC: approx {1} bytes in use", when, GC.GetTotalMemory(false));
for (int i = 0; i <= GC.MaxGeneration; i++)
{
log.DebugFormat("Number of times GC has occurred for generation {0} objects: {1}", i, GC.CollectionCount(i));
}
log.Debug("GDI objects in use: " + Win32.GetGuiResourcesGDICount(Process.GetCurrentProcess().Handle));
log.Debug("USER objects in use: " + Win32.GetGuiResourcesUserCount(Process.GetCurrentProcess().Handle));
}
void connection_ConnectionReconnecting(object sender, EventArgs e)
{
if (Program.Exiting)
return;
RequestRefreshTreeView();
gc();
}
private List<Host> hostsInInvalidState = new List<Host>();
// called whenever Xen objects on the server change state
void Connection_XenObjectsUpdated(object sender, EventArgs e)
{
if (Program.Exiting)
return;
IXenConnection connection = (IXenConnection) sender;
if (hostsInInvalidState.Count > 0)
{
foreach (var host in hostsInInvalidState.Where(host => host.Connection == connection))
CheckMaintenanceMode(host);
hostsInInvalidState.RemoveAll(host => host.Connection == connection);
}
RequestRefreshTreeView();
}
private int ignoreUpdateToolbars = 0;
private bool calledUpdateToolbars = false;
/// <summary>
/// Requests a refresh of the main tree view. The refresh will be managed such that we are not overloaded using an UpdateManager.
/// </summary>
public void RequestRefreshTreeView()
{
Program.Invoke(this, navigationPane.RequestRefreshTreeView);
}
private void UpdateHeaderAndTabPages()
{
Program.Invoke(this, () =>
{
// This is required to update search results when things change.
if (TheTabControl.SelectedTab == TabPageGeneral)
GeneralPage.BuildList();
else if (TheTabControl.SelectedTab == TabPageSearch)
SearchPage.BuildList();
UpdateHeader();
});
}
void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private bool _menuShortcuts = true;
public bool MenuShortcuts
{
set
{
if (value != _menuShortcuts)
{
//if the VNC Console is active (the user is typing into it etc) all of the shortcuts for XenCenter are disabled
//IMPORTANT! add any shortcuts you want to pass to the VNC console into this if, else statement
_menuShortcuts = value;
// update the selection so menu items can enable/disable keyboard shortcuts as appropriate.
SelectionManager.RefreshSelection();
}
}
}
/// <summary>
/// Must be called on the event thread.
/// </summary>
public void UpdateToolbars()
{
Program.AssertOnEventThread();
if (ignoreUpdateToolbars > 0)
{
calledUpdateToolbars = true;
return;
}
ToolStrip.SuspendLayout();
UpdateToolbarsCore();
MainMenuBar_MenuActivate(null, null);
ToolStrip.ResumeLayout();
}
private static int TOOLBAR_HEIGHT = 31;
/// <summary>
/// Updates the toolbar buttons. Also updates which tabs are visible.
/// </summary>
public void UpdateToolbarsCore()
{
// refresh the selection-manager
SelectionManager.RefreshSelection();
ToolStrip.Height = ToolbarsEnabled ? TOOLBAR_HEIGHT : 0;
ToolStrip.Enabled = ToolbarsEnabled;
ShowToolbarMenuItem.Checked = toolbarToolStripMenuItem.Checked = ToolbarsEnabled;
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;
IXenConnection selectionConnection = SelectionManager.Selection.GetConnectionOfFirstItem();
Pool selectionPool = selectionConnection == null ? null : Helpers.GetPool(selectionConnection);
Host selectionMaster = null == selectionPool ? null : selectionPool.Connection.Resolve(selectionPool.master);
// 'Home' tab is only visible if the 'Overview' tree node is selected, or if the tree is
// empty (i.e. at startup).
bool show_home = SelectionManager.Selection.Count == 1 && SelectionManager.Selection[0].Value == null;
// The upsell pages use the first selected XenObject: but they're only shown if there is only one selected object (see calls to ShowTab() below).
bool dmc_upsell = Helpers.FeatureForbidden(SelectionManager.Selection.FirstAsXenObject, Host.RestrictDMC);
bool ha_upsell = Helpers.FeatureForbidden(SelectionManager.Selection.FirstAsXenObject, Host.RestrictHA);
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.FirstIsPool;
bool isVMSelected = SelectionManager.Selection.FirstIsVM;
bool isHostSelected = SelectionManager.Selection.FirstIsHost;
bool isSRSelected = SelectionManager.Selection.FirstIsSR;
bool isRealVMSelected = SelectionManager.Selection.FirstIsRealVM;
bool isTemplateSelected = SelectionManager.Selection.FirstIsTemplate;
bool isHostLive = SelectionManager.Selection.FirstIsLiveHost;
bool 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;
NewTabCount = 0;
ShowTab(TabPageHome, !SearchMode && show_home);
ShowTab(TabPageGeneral, !multi && !SearchMode && (isVMSelected || (isHostSelected && (isHostLive || !is_connected)) || isPoolSelected || isSRSelected || isDockerContainerSelected));
ShowTab(dmc_upsell ? TabPageBallooningUpsell : TabPageBallooning, !multi && !SearchMode && (isVMSelected || (isHostSelected && isHostLive) || isPoolSelected));
ShowTab(TabPageStorage, !multi && !SearchMode && (isRealVMSelected || (isTemplateSelected && !selectedTemplateHasProvisionXML)));
ShowTab(TabPageSR, !multi && !SearchMode && isSRSelected);
ShowTab(TabPagePhysicalStorage, !multi && !SearchMode && ((isHostSelected && isHostLive) || isPoolSelected));
ShowTab(TabPageNetwork, !multi && !SearchMode && (isVMSelected || (isHostSelected && isHostLive) || isPoolSelected));
ShowTab(TabPageNICs, !multi && !SearchMode && ((isHostSelected && isHostLive)));
ShowTab(TabPageDockerProcess, !multi && !SearchMode && isDockerContainerSelected && !(SelectionManager.Selection.First as DockerContainer).Parent.IsWindows);
ShowTab(TabPageDockerDetails, !multi && !SearchMode && isDockerContainerSelected);
bool isPoolOrLiveStandaloneHost = isPoolSelected || (isHostSelected && isHostLive && selectionPool == null);
ShowTab(TabPageGPU, !multi && !SearchMode && ((isHostSelected && isHostLive) || isPoolOrLiveStandaloneHost) && Helpers.ClearwaterSp1OrGreater(selectionConnection) && !Helpers.FeatureForbidden(selectionConnection, Host.RestrictGpu));
pluginManager.SetSelectedXenObject(SelectionManager.Selection.FirstAsXenObject);
bool shownConsoleReplacement = false;
foreach (TabPageFeature f in pluginManager.GetAllFeatures<TabPageFeature>(f => f.IsConsoleReplacement && !f.IsError && !multi && f.ShowTab))
{
ShowTab(f.TabPage, true);
shownConsoleReplacement = true;
}
ShowTab(TabPageConsole, !shownConsoleReplacement && !multi && !SearchMode && (isRealVMSelected || (isHostSelected && isHostLive)));
ShowTab(TabPageCvmConsole, !shownConsoleReplacement && !multi && !SearchMode && isHostLive && hasManyControlDomains);
ShowTab(TabPagePeformance, !multi && !SearchMode && (isRealVMSelected || (isHostSelected && isHostLive)));
ShowTab(ha_upsell ? TabPageHAUpsell : TabPageHA, !multi && !SearchMode && isPoolSelected);
ShowTab(TabPageSnapshots, !multi && !SearchMode && isRealVMSelected);
//Any Clearwater XenServer, or WLB is not licensed on XenServer, the WLB tab and any WLB menu items disappear completely.
if(!(SelectionManager.Selection.All(s => Helpers.IsClearwater(s.Connection)) || wlb_upsell ))
ShowTab(TabPageWLB, !multi && !SearchMode && isPoolSelected);
ShowTab(ad_upsell ? TabPageADUpsell : TabPageAD, !multi && !SearchMode && (isPoolSelected || isHostSelected && isHostLive));
bool hasPvsSites = selectionConnection != null && selectionConnection.Cache.PVS_sites.Length > 0;
ShowTab(TabPagePvs, !multi && !SearchMode && isPoolOrLiveStandaloneHost && !Helpers.FeatureForbidden(SelectionManager.Selection.FirstAsXenObject, Host.RestrictPvsCache));
foreach (TabPageFeature f in pluginManager.GetAllFeatures<TabPageFeature>(f => !f.IsConsoleReplacement && !multi && f.ShowTab))
ShowTab(f.TabPage, true);
ShowTab(TabPageSearch, true);
// N.B. Change NewTabs definition if you add more tabs here.
// Save and restore focus on treeView, since selecting tabs in ChangeToNewTabs() has the
// unavoidable side-effect of giving them focus - this is irritating if trying to navigate
// the tree using the keyboard.
navigationPane.SaveAndRestoreTreeViewFocus(ChangeToNewTabs);
}
private readonly TabPage[] NewTabs = new TabPage[512];
int NewTabCount;
private void ShowTab(TabPage page, bool visible)
{
if (visible)
{
NewTabs[NewTabCount] = page;
NewTabCount++;
}
}
private void ChangeToNewTabs()
{
TabPage new_selected_page = NewSelectedPage();
TheTabControl.SuspendLayout();
IgnoreTabChanges = true;
try
{
TabControl.TabPageCollection p = TheTabControl.TabPages;
int i = 0; // Index into NewTabs
int m = 0; // Index into p
while (i < NewTabCount)
{
if (m == p.Count)
{
p.Add(NewTabs[i]);
if (new_selected_page == NewTabs[i])
TheTabControl.SelectedTab = new_selected_page;
m++;
i++;
}
else if (p[m] == NewTabs[i])
{
if (new_selected_page == NewTabs[i])
TheTabControl.SelectedTab = new_selected_page;
m++;
i++;
}
else if (NewTabs.Contains(p[m]))
{
p.Insert(m, NewTabs[i]);
if (new_selected_page == NewTabs[i])
TheTabControl.SelectedTab = new_selected_page;
m++;
i++;
}
else
{
if (TheTabControl.SelectedTab == p[m] && new_selected_page == NewTabs[i])
{
// This clause is deliberately targeted at the case when you go from
// Overview:Home to Host:Overview.
p.Insert(m, NewTabs[i]);
TheTabControl.SelectedTab = new_selected_page;
m++;
i++;
}
p.Remove(p[m]);
}
}
// Remove any tabs that are left at the end of the list.
while (m < p.Count)
{
TabPage removed = p[p.Count - 1];
p.Remove(removed);
int index = NewTabsIndexOf(removed);
if (index != -1)
{
// If this is a tab that we want, then we've got it in the list twice -- one
// reference was here when we entered this function, and is the one that we're
// pointing at now, and the other reference we've inserted through the loop above.
// This is bad -- p.Remove(removed) above has now invalidated removed.Parent
// (removed is still in the list through the other reference). Fix this up by
// removing the other reference too and starting over.
// We can't do the Remove call in the loop above, because this has a poor visual
// effect.
p.Remove(removed);
p.Insert(index, removed);
if (new_selected_page == removed)
TheTabControl.SelectedTab = removed;
}
}
}
finally
{
IgnoreTabChanges = false;
TheTabControl.ResumeLayout();
SetLastSelectedPage(SelectionManager.Selection.First, TheTabControl.SelectedTab);
}
}
private TabPage NewSelectedPage()
{
Object o = SelectionManager.Selection.First;
IXenObject s = o as IXenObject;
TabPage last_selected_page = GetLastSelectedPage(o);
return last_selected_page != null && NewTabs.Contains(last_selected_page)
? last_selected_page
: NewTabs[0];
}
private void SetLastSelectedPage(object o, TabPage p)
{
if (SearchMode)
return;
if (o == null)
{
selectedOverviewTab = p;
}
else
{
selectedTabs[o] = p;
}
}
private TabPage GetLastSelectedPage(object o)
{
return
o == null ? selectedOverviewTab :
selectedTabs.ContainsKey(o) ? selectedTabs[o] :
null;
}
private int NewTabsIndexOf(TabPage tp)
{
for (int i = 0; i < NewTabCount; i++)
{
if (NewTabs[i] == tp)
return i;
}
return -1;
}
private void pluginManager_PluginsChanged()
{
foreach (ToolStripMenuItem menu in MainMenuBar.Items)
{
//clear existing plugin items
for (int i = menu.DropDownItems.Count - 1; i >= 0; i--)
{
CommandToolStripMenuItem commandMenuItem = menu.DropDownItems[i] as CommandToolStripMenuItem;
if (commandMenuItem != null && (commandMenuItem.Command is MenuItemFeatureCommand
|| commandMenuItem.Command is ParentMenuItemFeatureCommand))
{
menu.DropDownItems.RemoveAt(i);
if (menu.DropDownItems.Count > 0 && menu.DropDownItems[i] is ToolStripSeparator)
menu.DropDownItems.RemoveAt(i);
}
}
// get insert index using the placeholder
int insertIndex = pluginMenuItemStartIndexes[menu];
bool itemAdded = false;
// add plugin items for this menu at insertIndex
foreach (PluginDescriptor plugin in pluginManager.Plugins)
{
if (!plugin.Enabled)
continue;
foreach (Feature feature in plugin.Features)
{
var menuItemFeature = feature as MenuItemFeature;
if (menuItemFeature != null && menuItemFeature.ParentFeature == null && (int)menuItemFeature.Menu == MainMenuBar.Items.IndexOf(menu))
{
Command cmd = menuItemFeature.GetCommand(this, SelectionManager.Selection);
menu.DropDownItems.Insert(insertIndex, new CommandToolStripMenuItem(cmd));
insertIndex++;
itemAdded = true;
}
var parentMenuItemFeature = feature as ParentMenuItemFeature;
if (parentMenuItemFeature != null && (int)parentMenuItemFeature.Menu == MainMenuBar.Items.IndexOf(menu))
{
Command cmd = parentMenuItemFeature.GetCommand(this, SelectionManager.Selection);
CommandToolStripMenuItem parentMenuItem = new CommandToolStripMenuItem(cmd);
menu.DropDownItems.Insert(insertIndex, parentMenuItem);
insertIndex++;
itemAdded = true;
foreach (MenuItemFeature childFeature in parentMenuItemFeature.Features)
{
Command childCommand = childFeature.GetCommand(this, SelectionManager.Selection);
parentMenuItem.DropDownItems.Add(new CommandToolStripMenuItem(childCommand));
}
}
}
}
if (itemAdded && insertIndex != menu.DropDownItems.Count)
menu.DropDownItems.Insert(insertIndex, new ToolStripSeparator());
}
}
private void MainMenuBar_MenuActivate(object sender, EventArgs e)
{
Host hostAncestor = SelectionManager.Selection.Count == 1 ? SelectionManager.Selection[0].HostAncestor : null;
IXenConnection connection = SelectionManager.Selection.GetConnectionOfFirstItem();
bool vm = SelectionManager.Selection.FirstIsRealVM && !((VM)SelectionManager.Selection.First).Locked;
exportSettingsToolStripMenuItem.Enabled = ConnectionsManager.XenConnectionsCopy.Count > 0;
this.MenuShortcuts = 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;
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");
}
/// <param name="e">
/// If null, then we deduce the method was called by TreeView_AfterSelect
/// and don't focus the VNC console. i.e. we only focus the VNC console if the user
/// explicitly clicked on the console tab rather than arriving there by navigating
/// in treeView.
/// </param>
private void TheTabControl_SelectedIndexChanged(object sender, EventArgs e)
{
if (IgnoreTabChanges)
return;
TabPage t = TheTabControl.SelectedTab;
if (!SearchMode)
History.NewHistoryItem(new XenModelObjectHistoryItem(SelectionManager.Selection.FirstAsXenObject, t));
if (t != TabPageBallooning)
BallooningPage.IsHidden();
if (t == TabPageConsole)
{
if (SelectionManager.Selection.FirstIsRealVM)
{
ConsolePanel.setCurrentSource((VM)SelectionManager.Selection.First);
UnpauseVNC(e != null && sender == TheTabControl);
}
else if (SelectionManager.Selection.FirstIsHost)
{
ConsolePanel.setCurrentSource((Host)SelectionManager.Selection.First);
UnpauseVNC(e != null && sender == TheTabControl);
}
}
else if (t == TabPageCvmConsole)
{
if (SelectionManager.Selection.FirstIsHost)
{
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)
{
SrStoragePage.SR = SelectionManager.Selection.First as SR;
}
else if (t == TabPageNetwork)
{
NetworkPage.XenObject = SelectionManager.Selection.FirstAsXenObject;
}
else if (t == TabPageNICs)
{
NICPage.Host = SelectionManager.Selection.First as Host;
}
else if (t == TabPageStorage)
{
VMStoragePage.VM = SelectionManager.Selection.First as VM;
}
else if (t == TabPagePeformance)
{
PerformancePage.XenObject = SelectionManager.Selection.FirstAsXenObject;
}
else if (t == TabPageSearch && !SearchMode)
{
var rootNode = SelectionManager.Selection.RootNode;
var rootNodeGrouping = rootNode == null ? null : rootNode.Tag as GroupingTag;
var search = rootNode == null ? null : rootNode.Tag as Search;
if (search != null)
{
SearchPage.Search = search;
}
else if (rootNodeGrouping != null)
{
var objectsView = rootNodeGrouping.Grouping as OrganizationViewObjects;
var vappsView = rootNodeGrouping.Grouping as OrganizationViewVapps;
var foldersView = rootNodeGrouping.Grouping as OrganizationViewFolders;
if (vappsView != null)
{
SearchPage.Search = Search.SearchForVappGroup(rootNodeGrouping.Grouping,
rootNodeGrouping.Parent, rootNodeGrouping.Group);
}
else if (objectsView != null)
{
//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 == TabPagePeformance)
PerformancePage.ResumeGraph();
else
PerformancePage.PauseGraph();
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);
}
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
{
Overview, Home, Settings, Storage, Network, Console, CvmConsole, Performance, NICs, SR, DockerProcess, DockerDetails
}
public void SwitchToTab(Tab tab)
{
switch (tab)
{
case Tab.Overview:
TheTabControl.SelectedTab = TabPageSearch;
break;
case Tab.Home:
TheTabControl.SelectedTab = TabPageHome;
break;
case Tab.Settings:
TheTabControl.SelectedTab = TabPageGeneral;
break;
case Tab.Storage:
TheTabControl.SelectedTab = TabPageStorage;
break;
case Tab.Network:
TheTabControl.SelectedTab = TabPageNetwork;
break;
case Tab.Console:
TheTabControl.SelectedTab = TabPageConsole;
break;
case Tab.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;
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 void ShowForm(Type type)
{
ShowForm(type, null);
}
/// <summary>
/// Shows a form of the specified type if it has already been created. If the form doesn't exist yet
/// it is created first and then shown.
/// </summary>
/// <param name="type">The type of the form to be shown.</param>
/// <param name="args">The arguments to pass to the form's consructor</param>
public void ShowForm(Type type, object[] args)
{
foreach (Form form in Application.OpenForms)
{
if (form.GetType() == type)
{
HelpersGUI.BringFormToFront(form);
return;
}
}
Form newForm = (Form)Activator.CreateInstance(type, args);
newForm.Show(this);
}
public Form Form
{
get { return this; }
}
public void Invoke(MethodInvoker method)
{
Program.Invoke(this, method);
}
public bool SelectObjectInTree(IXenObject xenObject)
{
return SelectObject(xenObject);
}
public Collection<IXenConnection> GetXenConnectionsCopy()
{
return new Collection<IXenConnection>(ConnectionsManager.XenConnectionsCopy);
}
public void SaveServerList()
{
Settings.SaveServerList();
}
public bool RunInAutomatedTestMode
{
get { return Program.RunInAutomatedTestMode; }
}
public void RemoveConnection(IXenConnection connection)
{
ConnectionsManager.ClearCacheAndRemoveConnection(connection);
}
public void PutSelectedNodeIntoEditMode()
{
EditSelectedNodeInTreeView();
}
public void TrySelectNewObjectInTree(Predicate<object> tagMatch, bool selectNode, bool expandNode, bool ensureNodeVisible)
{
TrySelectNewNode(tagMatch, selectNode, expandNode, ensureNodeVisible);
}
public void TrySelectNewObjectInTree(IXenConnection c, bool selectNode, bool expandNode, bool ensureNodeVisible)
{
TrySelectNewNode(c, selectNode, expandNode, ensureNodeVisible);
}
public bool MenuShortcutsEnabled
{
get { return _menuShortcuts; }
}
#endregion
#region Help
private string TabHelpID()
{
string modelObj = getSelectedXenModelObjectType();
if (TheTabControl.SelectedTab == TabPageHome)
return "TabPageHome" + modelObj;
if (TheTabControl.SelectedTab == TabPageSearch)
return "TabPageSearch" + modelObj;
if (TheTabControl.SelectedTab == TabPageConsole)
return "TabPageConsole" + modelObj;
if (TheTabControl.SelectedTab == 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 == TabPageBallooningUpsell)
return "TabPageBallooningUpsell";
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;
return "TabPageUnknown";
}
private string getSelectedXenModelObjectType()
{
// for now, since there are few topics which depend on the selected object we shall just check the special cases
// when more topic are added we can just return the ModelObjectName
if (TheTabControl.SelectedTab == TabPageGeneral && SelectionManager.Selection.First is VM)
{
return "VM";
}
if (TheTabControl.SelectedTab == TabPagePhysicalStorage || TheTabControl.SelectedTab == TabPageStorage || TheTabControl.SelectedTab == TabPageSR)
{
if (SelectionManager.Selection.FirstIsPool)
return "Pool";
if (SelectionManager.Selection.FirstIsHost)
return "Server";
if (SelectionManager.Selection.FirstIsVM)
return "VM";
if (SelectionManager.Selection.FirstIsSR)
return "Storage";
}
if (TheTabControl.SelectedTab == TabPageNetwork)
{
if (SelectionManager.Selection.FirstIsHost)
return "Server";
if (SelectionManager.Selection.FirstIsVM)
return "VM";
}
return "";
}
public void ShowHelpTOC()
{
ShowHelpTopic(null);
}
public void ShowHelpTopic(string topicID)
{
// Abandon all hope, ye who enter here: if you're ever tempted to directly invoke hh.exe, see first:
// JAXC-43: Online help doesn't work if install XenCenter into the path that contains special characters.
// hh.exe can't seem to cope with certain multi-byte characters in the path to the chm.
// System.Windows.Forms.Help.ShowHelp() can cope with the special characters in the path, but has the
// irritating behaviour that the launched help is always on top of the app window (CA-8863).
// So we show the help 'on top' of an invisible dummy Form.
using (var helpForm = new Form())
{
string chm = Path.Combine(Program.AssemblyDir, InvisibleMessages.MAINWINDOW_HELP_PATH);
if (topicID == null)
{
// Show TOC
System.Windows.Forms.Help.ShowHelp(helpForm, chm, HelpNavigator.TableOfContents);
}
else
{
System.Windows.Forms.Help.ShowHelp(helpForm, chm, HelpNavigator.TopicId, topicID);
}
}
}
public void MainWindow_HelpRequested(object sender, HelpEventArgs hlpevent)
{
// CA-28064. MessageBox hack to kill the hlpevent it passes to MainWindows.
if (Program.MainWindow.ContainsFocus && _menuShortcuts)
LaunchHelp();
}
private void helpTopicsToolStripMenuItem_Click(object sender, EventArgs e)
{
ShowHelpTOC();
}
private void helpContextMenuItem_Click(object sender, EventArgs e)
{
LaunchHelp();
}
private void LaunchHelp()
{
if (alertPage.Visible)
Help.HelpManager.Launch("AlertSummaryDialog");
else if (updatesPage.Visible)
Help.HelpManager.Launch("ManageUpdatesDialog");
else if (eventsPage.Visible)
Help.HelpManager.Launch("EventsPane");
else
{
if (TheTabControl.SelectedTab.Tag is TabPageFeature && ((TabPageFeature)TheTabControl.SelectedTab.Tag).HasHelp)
((TabPageFeature)TheTabControl.SelectedTab.Tag).LaunchHelp();
else
Help.HelpManager.Launch(TabHelpID());
}
}
public bool HasHelp()
{
return Help.HelpManager.HasHelpFor(TabHelpID());
}
private void viewApplicationLogToolStripMenuItem_Click(object sender, EventArgs e)
{
Program.ViewLogFiles();
}
#endregion
/// <summary>
/// Used to select the pool or standalone host node for the specified connection which is about to appear in the tree.
/// </summary>
/// <param name="connection">The connection.</param>
/// <param name="selectNode">if set to <c>true</c> then the pool/standalone host node will be selected.</param>
/// <param name="expandNode">if set to <c>true</c> then the pool/standalone host node will be expanded.</param>
/// <param name="ensureNodeVisible">if set to <c>true</c> then the matched node will be made visible.</param>
public void TrySelectNewNode(IXenConnection connection, bool selectNode, bool expandNode, bool ensureNodeVisible)
{
if (connection != null)
{
TrySelectNewNode(delegate(object o)
{
if (o == null)
{
return false;
}
else if (o.Equals(Helpers.GetPool(connection)))
{
return true;
}
Host[] hosts = connection.Cache.Hosts;
return hosts.Length > 0 && o.Equals(hosts[0]);
}, selectNode, expandNode, ensureNodeVisible);
}
}
/// <summary>
/// Used to select or expand a node which is about to appear in the tree. This is used so that new hosts, folders, pools
/// etc. can be picked and then selected/expanded.
///
/// It fires off a new thread and then repeatedly tries to select a node which matches the specified match
/// delegate. It stops if it times out or is successful.
/// </summary>
/// <param name="tagMatch">A match for the tag of the node.</param>
/// <param name="selectNode">if set to <c>true</c> then the matched node will be selected.</param>
/// <param name="expandNode">if set to <c>true</c> then the matched node will be expanded.</param>
/// <param name="ensureNodeVisible">if set to <c>true</c> then the matched node will be made visible.</param>
public void TrySelectNewNode(Predicate<object> tagMatch, bool selectNode, bool expandNode, bool ensureNodeVisible)
{
ThreadPool.QueueUserWorkItem(delegate
{
bool success = false;
for (int i = 0; i < 20 && !success; i++)
{
Program.Invoke(Program.MainWindow, delegate
{
success = navigationPane.TryToSelectNewNode(tagMatch, selectNode, expandNode, ensureNodeVisible);
});
Thread.Sleep(500);
}
});
}
/// <summary>
/// Selects the specified object in the treeview.
/// </summary>
/// <param name="o">The object to be selected.</param>
/// <returns>A value indicating whether selection was successful.</returns>
public bool SelectObject(IXenObject o)
{
return navigationPane.SelectObject(o);
}
private void eventsPage_GoToXenObjectRequested(IXenObject obj)
{
navigationPane.SwitchToInfrastructureMode();
navigationPane.SelectObject(obj);
}
private void Updates_CollectionChanged(object sender, CollectionChangeEventArgs e)
{
Program.Invoke(this, () =>
{
int updatesCount = Updates.UpdateAlertsCount;
navigationPane.UpdateNotificationsButton(NotificationsSubMode.Updates, updatesCount);
if (updatesPage.Visible)
{
TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Updates, updatesCount);
TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Updates, updatesCount);
}
});
}
private void CloseWhenActionsCanceled(object o)
{
int i = 0;
while (true)
{
if (i > 20)
Program.ForcedExiting = true;
if (i > 40 || AllActionsCompleted())
{
Program.Invoke(this, Application.Exit);
break;
}
i++;
System.Threading.Thread.Sleep(500);
}
}
private bool AllActionsCompleted()
{
foreach (ActionBase a in ConnectionsManager.History)
{
if (!a.IsCompleted)
return false;
}
return true;
}
private void preferencesToolStripMenuItem_Click(object sender, EventArgs e)
{
OptionsDialog dialog = new OptionsDialog(pluginManager);
dialog.ShowDialog(this);
}
internal void action_Completed(ActionBase sender)
{
if (Program.Exiting)
{
return;
}
RequestRefreshTreeView();
//Update toolbars, since if an action has just completed, various
//buttons may need to be re-enabled. Applies to:
// HostAction
// EnableHAAction
// DisableHAAction
Program.Invoke(this, UpdateToolbars);
}
internal void OpenGlobalImportWizard(string param)
{
HelpersGUI.BringFormToFront(this);
Host hostAncestor = SelectionManager.Selection.Count == 1 ? SelectionManager.Selection[0].HostAncestor : null;
new ImportWizard(SelectionManager.Selection.GetConnectionOfFirstItem(), hostAncestor, param, false).Show();
}
internal void InstallUpdate(string path)
{
var wizard = new PatchingWizard();
wizard.Show(this);
wizard.NextStep();
wizard.AddFile(path);
}
#region XenSearch
// SearchMode doesn't just mean we are looking at the Search tab.
// It's set when we import a search from a file; or when we double-click
// on a folder or tag name to search for it.
private bool searchMode = false;
public bool SearchMode
{
get
{
return searchMode;
}
set
{
if (searchMode == value)
return;
searchMode = value;
navigationPane.InSearchMode = value;
UpdateToolbarsCore();
}
}
public bool DoSearch(string filename)
{
List<Search> searches = Search.LoadFile(filename);
if (searches != null && searches.Count > 0)
{
Program.Invoke(Program.MainWindow, delegate()
{
DoSearch(searches[0]);
});
return true;
}
return false;
}
public void DoSearch(Search search)
{
History.NewHistoryItem(new SearchHistoryItem(search));
SearchMode = true;
SearchPage.Search = search;
UpdateHeader();
}
public void SearchForTag(string tag)
{
DoSearch(Search.SearchForTag(tag));
}
public void SearchForFolder(string path)
{
DoSearch(Search.SearchForFolder(path));
}
void SearchPanel_SearchChanged()
{
if (SearchMode)
History.ReplaceHistoryItem(new SearchHistoryItem(SearchPage.Search));
else
History.ReplaceHistoryItem(new ModifiedSearchHistoryItem(
SelectionManager.Selection.FirstAsXenObject, SearchPage.Search));
}
/// <summary>
/// Updates the shiny gradient bar with selected object name and icon.
/// Also updates 'Logged in as:'.
/// </summary>
private void UpdateHeader()
{
if (navigationPane.currentMode == NavigationPane.NavigationMode.Notifications)
return;
if (SearchMode && SearchPage.Search != null)
{
TitleLabel.Text = HelpersGUI.GetLocalizedSearchName(SearchPage.Search);
TitleIcon.Image = Images.GetImage16For(SearchPage.Search);
}
else if (!SearchMode && SelectionManager.Selection.ContainsOneItemOfType<IXenObject>())
{
IXenObject xenObject = SelectionManager.Selection[0].XenObject;
TitleLabel.Text = xenObject.NameWithLocation;
TitleIcon.Image = Images.GetImage16For(xenObject);
// When in folder view only show the logged in label if it is clear to which connection the object belongs (most likely pools and hosts)
if (SelectionManager.Selection[0].PoolAncestor == null && SelectionManager.Selection[0].HostAncestor == null)
loggedInLabel1.Connection = null;
else
loggedInLabel1.Connection = xenObject.Connection;
}
else
{
TitleLabel.Text = Messages.XENCENTER;
TitleIcon.Image = Properties.Resources.Logo;
loggedInLabel1.Connection = null;
}
}
private void UpdateViewMenu(NavigationPane.NavigationMode mode)
{
//the order is the reverse from the order in which we want them to appear
var items = new ToolStripItem []
{
toolStripSeparator24,
ShowHiddenObjectsToolStripMenuItem,
localStorageToolStripMenuItem,
templatesToolStripMenuItem1,
customTemplatesToolStripMenuItem
};
if (mode == NavigationPane.NavigationMode.Infrastructure)
{
foreach (var item in items)
{
if (!viewToolStripMenuItem.DropDownItems.Contains(item))
viewToolStripMenuItem.DropDownItems.Insert(0, item);
}
}
else if (mode == NavigationPane.NavigationMode.Notifications)
{
foreach (var item in items)
viewToolStripMenuItem.DropDownItems.Remove(item);
}
else
{
for (int i = 2; i < items.Length; i++)
viewToolStripMenuItem.DropDownItems.Remove(items[i]);
for (int i = 0; i < 2; i++)
if (!viewToolStripMenuItem.DropDownItems.Contains(items[i]))
viewToolStripMenuItem.DropDownItems.Insert(0, items[i]);
}
pluginMenuItemStartIndexes[viewToolStripMenuItem] = viewToolStripMenuItem.DropDownItems.IndexOf(toolStripSeparator24) + 1;
}
void navigationPane_DragDropCommandActivated(string cmdText)
{
SetStatusBar(null, cmdText);
}
private void navigationPane_TreeViewSelectionChanged()
{
UpdateToolbars();
//
// NB do not trigger updates to the panels in this method
// instead, put them in TheTabControl_SelectedIndexChanged,
// so only the selected tab is updated
//
TheTabControl_SelectedIndexChanged(null, EventArgs.Empty);
if (TheTabControl.SelectedTab != null)
TheTabControl.SelectedTab.Refresh();
UpdateHeader();
}
private void navigationPane_NotificationsSubModeChanged(NotificationsSubModeItem submodeItem)
{
alertPage.Visible = submodeItem.SubMode == NotificationsSubMode.Alerts;
updatesPage.Visible = submodeItem.SubMode == NotificationsSubMode.Updates;
eventsPage.Visible = submodeItem.SubMode == NotificationsSubMode.Events;
TheTabControl.Visible = false;
if (alertPage.Visible)
alertPage.RefreshAlertList();
if (updatesPage.Visible)
updatesPage.RefreshUpdateList();
if (eventsPage.Visible)
{
eventsPage.RefreshDisplayedEvents();
}
loggedInLabel1.Connection = null;
TitleLabel.Text = submodeItem.Text;
TitleIcon.Image = submodeItem.Image;
}
private void navigationPane_NavigationModeChanged(NavigationPane.NavigationMode mode)
{
if (mode == NavigationPane.NavigationMode.Notifications)
{
TheTabControl.Visible = false;
}
else
{
bool tabControlWasVisible = TheTabControl.Visible;
TheTabControl.Visible = true;
alertPage.Visible = updatesPage.Visible = eventsPage.Visible = false;
// force an update of the selected tab when switching back from Notification view,
// as some tabs ignore the update events when not visible (e.g. Snapshots, HA)
if (!tabControlWasVisible)
TheTabControl_SelectedIndexChanged(null, null);
}
UpdateViewMenu(mode);
}
private void navigationPane_TreeNodeBeforeSelected()
{
SearchMode = false;
}
private void navigationPane_TreeNodeClicked()
{
if (SearchMode)
{
SearchMode = false;
TheTabControl_SelectedIndexChanged(null, null);
UpdateHeader();
}
}
private void navigationPane_TreeNodeRightClicked()
{
MainMenuBar_MenuActivate(MainMenuBar, new EventArgs());
}
private void navigationPane_TreeViewRefreshed()
{
UpdateHeaderAndTabPages();
}
private void navigationPane_TreeViewRefreshResumed()
{
ignoreUpdateToolbars--;
if (ignoreUpdateToolbars == 0 && calledUpdateToolbars)
UpdateToolbars();
}
private void navigationPane_TreeViewRefreshSuspended()
{
if (ignoreUpdateToolbars == 0)
calledUpdateToolbars = false;
ignoreUpdateToolbars++;
}
#endregion
void XenCenterAlerts_CollectionChanged(object sender, CollectionChangeEventArgs e)
{
Program.BeginInvoke(Program.MainWindow, () =>
{
navigationPane.UpdateNotificationsButton(
NotificationsSubMode.Alerts, Alert.NonDismissingAlertCount);
if (alertPage.Visible)
{
TitleLabel.Text = NotificationsSubModeItem.GetText(NotificationsSubMode.Alerts, Alert.NonDismissingAlertCount);
TitleIcon.Image = NotificationsSubModeItem.GetImage(NotificationsSubMode.Alerts, Alert.NonDismissingAlertCount);
}
});
}
private void backButton_Click(object sender, EventArgs e)
{
History.Back(1);
}
private void forwardButton_Click(object sender, EventArgs e)
{
History.Forward(1);
}
private void backButton_DropDownOpening(object sender, EventArgs e)
{
ToolStripSplitButton button = sender as ToolStripSplitButton;
if (button == null)
return;
History.PopulateBackDropDown(button);
}
private void forwardButton_DropDownOpening(object sender, EventArgs e)
{
ToolStripSplitButton button = sender as ToolStripSplitButton;
if (button == null)
return;
History.PopulateForwardDropDown(button);
}
private void LicenseManagerMenuItem_Click(object sender, EventArgs e)
{
licenseManagerLauncher.LaunchIfRequired(false, ConnectionsManager.XenConnections, SelectionManager.Selection);
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F5)
{
RequestRefreshTreeView();
if (TheTabControl.SelectedTab == TabPageSearch)
SearchPage.PanelProd();
}
}
private void ShowToolbarMenuItem_Click(object sender, EventArgs e)
{
ToolbarsEnabled = !ToolbarsEnabled;
Properties.Settings.Default.ToolbarsEnabled = ToolbarsEnabled;
UpdateToolbars();
}
private void MainMenuBar_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
ToolBarContextMenu.Show(Program.MainWindow, e.Location);
}
}
/// <summary>
/// Equivalent to MainController.Confirm(conn, this, msg, args).
/// </summary>
public bool Confirm(IXenConnection conn, string title, string msg, params object[] args)
{
return Confirm(conn, this, title, msg, args);
}
/// <summary>
/// Show a MessageBox asking to confirm an operation. The MessageBox will be parented to the given Control.
/// Displays default "Yes" and "No" buttons ("Yes" button is selected by default).
/// The args given will be ellipsised to Helpers.DEFAULT_NAME_TRIM_LENGTH, if they are strings.
/// If in automated test mode, then always returns true.
/// If the user refuses the operation, then returns false.
/// If the given connection has disconnected in the time it takes the user to confirm,
/// then shows an information MessageBox, and returns false.
/// Otherwise, the user has agreed and the connection is still alive, so
/// sets MainWindow.AllowHistorySwitch to true and returns true.
/// </summary>
public static bool Confirm(IXenConnection conn, Control parent, string title, string msg, params object[] args)
{
return Confirm(conn, parent, title, null, null, null, msg, args);
}
/// <summary>
/// Show a MessageBox asking to confirm an operation. The MessageBox will be parented to the given Control.
/// "Yes" and "No" buttons can be customized.
/// The args given will be ellipsised to Helpers.DEFAULT_NAME_TRIM_LENGTH, if they are strings.
/// If in automated test mode, then always returns true.
/// If the user refuses the operation, then returns false.
/// If the given connection has disconnected in the time it takes the user to confirm,
/// then shows an information MessageBox, and returns false.
/// Otherwise, the user has agreed and the connection is still alive, so
/// sets MainWindow.AllowHistorySwitch to true and returns true.
/// </summary>
public static bool Confirm(IXenConnection conn, Control parent, string title,
string helpName, ThreeButtonDialog.TBDButton buttonYes, ThreeButtonDialog.TBDButton buttonNo,
string msg, params object[] args)
{
if (Program.RunInAutomatedTestMode)
return true;
Trim(args);
var buttons = new[]
{
buttonYes ?? ThreeButtonDialog.ButtonYes,
buttonNo ?? ThreeButtonDialog.ButtonNo
};
var details = new ThreeButtonDialog.Details(SystemIcons.Exclamation,
args.Length == 0 ? msg : string.Format(msg, args), title);
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();
}
private void MainWindow_Resize(object sender, EventArgs e)
{
SetSplitterDistance();
}
private void SetSplitterDistance()
{
//CA-71697: chosen min size so the tab contents are visible
int chosenPanel2MinSize = MinimumSize.Width / 2;
int min = splitContainer1.Panel1MinSize;
int max = splitContainer1.Width - chosenPanel2MinSize;
if (max < min)
return;
splitContainer1.Panel2MinSize = chosenPanel2MinSize;
if (splitContainer1.SplitterDistance < min)
splitContainer1.SplitterDistance = min;
else if (splitContainer1.SplitterDistance > max)
splitContainer1.SplitterDistance = max;
}
}
}