/* 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.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.Wizards.ImportWizard;
using XenAPI;
using XenAdmin.Actions;
using XenAdmin.Alerts;
using XenAdmin.Commands;
using XenAdmin.Controls;
using XenAdmin.Core;
using XenAdmin.Dialogs;
using XenAdmin.Model;
using XenAdmin.Network;
using XenAdmin.TabPages;
using XenAdmin.XenSearch;
using XenAdmin.Wizards.PatchingWizard;
using XenAdmin.Plugins;
using XenAdmin.Network.StorageLink;
using System.Linq;
[assembly: UIPermission(SecurityAction.RequestMinimum, Clipboard = UIPermissionClipboard.AllClipboard)]
namespace XenAdmin
{
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
[ComVisibleAttribute(true)]
public partial class MainWindow : Form, ISynchronizeInvoke
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
///
/// A mapping between objects in the tree and the associated selected tab.
///
private Dictionary selectedTabs = new Dictionary();
///
/// The selected tab for the overview node.
///
private TabPage selectedOverviewTab = null;
internal readonly PerformancePage PerformancePage = new PerformancePage();
internal readonly GeneralTabPage GeneralPage = new GeneralTabPage();
internal readonly BallooningPage BallooningPage = new BallooningPage();
internal readonly BallooningUpsellPage BallooningUpsellPage = new BallooningUpsellPage();
internal readonly ConsolePanel ConsolePanel = new ConsolePanel();
internal readonly HAPage HAPage = new HAPage();
internal readonly HAUpsellPage HAUpsellPage = new HAUpsellPage();
internal readonly HistoryPage HistoryPage = new HistoryPage();
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();
private bool IgnoreTabChanges = false;
public bool AllowHistorySwitch = false;
private bool ToolbarsEnabled;
private readonly Dictionary> activePoolWizards = new Dictionary>();
private readonly Dictionary activeXenModelObjectWizards = new Dictionary();
///
/// The arguments passed in on the command line.
///
private string[] CommandLineParam = null;
private ArgType CommandLineArgType = ArgType.None;
private static readonly Color HasAlertsColor = Color.Red;
private static readonly Color NoAlertsColor = SystemColors.ControlText;
private static readonly System.Windows.Forms.Timer CheckForUpdatesTimer = new System.Windows.Forms.Timer();
private readonly UpdateManager treeViewUpdateManager = new UpdateManager(30 * 1000);
private readonly MainWindowTreeBuilder treeBuilder;
private readonly SelectionManager selectionManager = new SelectionManager();
private readonly PluginManager pluginManager;
private readonly ContextMenuBuilder contextMenuBuilder;
private readonly MainWindowCommandInterface commandInterface;
private readonly LicenseManagerLauncher licenseManagerLauncher;
private readonly LicenseTimer licenseTimer;
private Dictionary pluginMenuItemStartIndexes = new Dictionary();
public MainWindow(ArgType argType, string[] args)
{
Program.MainWindow = this;
licenseManagerLauncher = new LicenseManagerLauncher(Program.MainWindow);
InvokeHelper.Initialize(this);
InitializeComponent();
SetMenuItemStartIndexes();
Icon = Properties.Resources.AppIcon;
treeView.ImageList = Images.ImageList16;
if (treeView.ItemHeight < 18)
treeView.ItemHeight = 18; // otherwise it's too close together on XP and the icons crash into each other
components.Add(SearchPage);
components.Add(NICPage);
components.Add(VMStoragePage);
components.Add(SrStoragePage);
components.Add(PerformancePage);
components.Add(GeneralPage);
components.Add(BallooningPage);
components.Add(ConsolePanel);
components.Add(NetworkPage);
components.Add(HAPage);
components.Add(HistoryPage);
components.Add(HomePage);
components.Add(WlbPage);
components.Add(AdPage);
AddTabContents(SearchPage, TabPageSearch);
AddTabContents(VMStoragePage, TabPageStorage);
AddTabContents(SrStoragePage, TabPageSR);
AddTabContents(NICPage, TabPageNICs);
AddTabContents(PerformancePage, TabPagePeformance);
AddTabContents(GeneralPage, TabPageGeneral);
AddTabContents(BallooningPage, TabPageBallooning);
AddTabContents(BallooningUpsellPage, TabPageBallooningUpsell);
AddTabContents(ConsolePanel, TabPageConsole);
AddTabContents(NetworkPage, TabPageNetwork);
AddTabContents(HAPage, TabPageHA);
AddTabContents(HAUpsellPage, TabPageHAUpsell);
AddTabContents(HistoryPage, TabPageHistory);
AddTabContents(HomePage, TabPageHome);
AddTabContents(WlbPage, TabPageWLB);
AddTabContents(WLBUpsellPage, TabPageWLBUpsell);
AddTabContents(PhysicalStoragePage, TabPagePhysicalStorage);
AddTabContents(AdPage, TabPageAD);
TheTabControl.SelectedIndexChanged += TheTabControl_SelectedIndexChanged;
PoolCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged);
MessageCollectionChangedWithInvoke = Program.ProgramInvokeHandler(MessageCollectionChanged);
HostCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged);
VMCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged);
SRCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged);
FolderCollectionChangedWithInvoke = Program.ProgramInvokeHandler(CollectionChanged);
TaskCollectionChangedWithInvoke = Program.ProgramInvokeHandler(MeddlingActionManager.TaskCollectionChanged);
ConnectionsManager.History.CollectionChanged += History_CollectionChanged;
CommandLineArgType = argType;
CommandLineParam = args;
commandInterface = new MainWindowCommandInterface(this);
pluginManager = new PluginManager();
pluginManager.LoadPlugins();
contextMenuBuilder = new ContextMenuBuilder(pluginManager, commandInterface);
TreeSearchBox.SearchChanged += TreeSearchBox_SearchChanged;
SearchPage.SearchChanged += SearchPanel_SearchChanged;
Alert.XenCenterAlerts.CollectionChanged += XenCenterAlerts_CollectionChanged;
FormFontFixer.Fix(this);
Folders.InitFolders();
OtherConfigAndTagsWatcher.InitEventHandlers();
// Fix colour of text on gradient panels
TitleLabel.ForeColor = Program.TitleBarForeColor;
loggedInLabel1.SetTextColor(Program.TitleBarForeColor);
TitleLeftLine.Visible = Environment.OSVersion.Version.Major != 6 || Application.RenderWithVisualStyles;
VirtualTreeNode n = new VirtualTreeNode(Messages.XENCENTER);
n.NodeFont = Program.DefaultFont;
treeView.Nodes.Add(n);
treeViewUpdateManager.Update += treeViewUpdateManager_Update;
treeBuilder = new MainWindowTreeBuilder(treeView);
selectionManager.BindTo(MainMenuBar.Items, commandInterface);
selectionManager.BindTo(ToolStrip.Items, commandInterface);
Properties.Settings.Default.SettingChanging += new System.Configuration.SettingChangingEventHandler(Default_SettingChanging);
licenseTimer = new LicenseTimer(licenseManagerLauncher);
GeneralPage.LicenseLauncher = licenseManagerLauncher;
}
private void Default_SettingChanging(object sender, System.Configuration.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)
{
ToolStripMenuItem menuItem = item as ToolStripMenuItem;
if (item != null && item.Text == "PluginItemsPlaceHolder")
{
pluginMenuItemStartIndexes.Add(menu, menu.DropDownItems.IndexOf(item));
menu.DropDownItems.Remove(item);
break;
}
}
}
}
internal SelectionBroadcaster SelectionManager
{
get
{
return selectionManager;
}
}
internal ContextMenuBuilder ContextMenuBuilder
{
get
{
return contextMenuBuilder;
}
}
internal IMainWindow CommandInterface
{
get
{
return commandInterface;
}
}
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();
ActiveControl = treeView;
}
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;
action.Changed += actionChanged;
action.Completed += actionChanged;
actionChanged(action);
});
}
void actionChanged(ActionBase action)
{
if (Program.Exiting)
return;
Program.Invoke(this, () => actionChanged_(action));
}
void actionChanged_(ActionBase action)
{
// Be defensive against CA-8517.
if (action.PercentComplete < 0 || action.PercentComplete > 100)
{
log.ErrorFormat("PercentComplete is erroneously {0}", action.PercentComplete);
}
// Don't show cancelled exception
if (action.Exception != null && !(action.Exception is CancelledException))
{
bool logs_visible;
IXenObject selected = selectionManager.Selection.FirstAsXenObject;
if (selected != null && action.AppliesTo.Contains(selected.opaque_ref))
{
if (TheTabControl.SelectedTab == TabPageHistory)
{
logs_visible = true;
}
else if (AllowHistorySwitch)
{
TheTabControl.SelectedTab = TabPageHistory;
logs_visible = true;
}
else
{
logs_visible = false;
}
}
else
{
logs_visible = false;
}
if (!logs_visible)
{
IXenObject model =
(IXenObject)action.VM ??
(IXenObject)action.Host ??
(IXenObject)action.Pool ??
(IXenObject)action.SR;
if (model != null)
model.InError = true;
}
RequestRefreshTreeView();
}
}
private void MainWindow_Shown(object sender, EventArgs e)
{
MainMenuBar.Location = new Point(0, 0);
treeView.SelectedNode = treeView.Nodes[0];
if (ToolStrip.Renderer is ToolStripProfessionalRenderer)
{
((ToolStripProfessionalRenderer)ToolStrip.Renderer).RoundedEdges = false;
}
ConnectionsManager.XenConnections.CollectionChanged += XenConnection_CollectionChanged;
try
{
Settings.RestoreSession();
}
catch (ConfigurationErrorsException ex)
{
log.Error("Could not load settings.", ex);
Program.CloseSplash();
new ThreeButtonDialog(
new ThreeButtonDialog.Details(
SystemIcons.Error,
string.Format(Messages.MESSAGEBOX_LOAD_CORRUPTED, Settings.GetUserConfigPath()),
Messages.MESSAGEBOX_LOAD_CORRUPTED_TITLE)).ShowDialog(this);
Application.Exit();
return; // Application.Exit() does not exit the current method.
}
ToolbarsEnabled = Properties.Settings.Default.ToolbarsEnabled;
RequestRefreshTreeView();
UpdateToolbars();
ExpandPanel(HistoryPage);
// kick-off connections for all the loaded server list
foreach (IXenConnection connection in ConnectionsManager.XenConnectionsCopy)
{
if (!connection.SaveDisconnected)
{
XenConnectionUI.BeginConnect(connection, true, this, true);
// if there are fewer than 30 connections, then expand the tree nodes.
if (ConnectionsManager.XenConnectionsCopy.Count < 30)
{
connection.CachePopulated += connection_CachePopulatedOnStartup;
}
}
}
Program.StorageLinkConnections.CollectionChanged += StorageLinkConnections_CollectionChanged;
RequestRefreshTreeView();
ThreadPool.QueueUserWorkItem((WaitCallback)delegate(object o)
{
// Sleep a short time before closing the splash
Thread.Sleep(500);
Program.Invoke(Program.MainWindow, Program.CloseSplash);
});
if (!Program.RunInAutomatedTestMode && !Helpers.CommonCriteriaCertificationRelease)
{
if (!Properties.Settings.Default.SeenAllowUpdatesDialog)
new AllowUpdatesDialog(pluginManager).ShowDialog(this);
// start checkforupdates thread
CheckForUpdatesTimer.Interval = 1000 * 60 * 60 * 24; // 24 hours
CheckForUpdatesTimer.Tick += Updates.Tick;
CheckForUpdatesTimer.Start();
Updates.AutomaticCheckForUpdates();
}
ProcessCommand(CommandLineArgType, CommandLineParam);
}
private void LoadTasksAsMeddlingActions(IXenConnection connection)
{
if (!connection.IsConnected || connection.Session == null)
return;
Dictionary, Task> tasks = Task.get_all_records(connection.Session);
foreach (KeyValuePair, Task> pair in tasks)
{
pair.Value.Connection = connection;
pair.Value.opaque_ref = pair.Key;
MeddlingActionManager.ForceAddTask(pair.Value);
}
}
private void StorageLinkConnections_CollectionChanged(object sender, CollectionChangeEventArgs e)
{
var con = ((StorageLinkConnection)e.Element);
if (e.Action == CollectionChangeAction.Add)
{
con.ConnectionStateChanged += (s, ee) =>
{
if (ee.ConnectionState == StorageLinkConnectionState.Connected)
{
Program.Invoke(this, RefreshTreeView);
UpdateHeaderAndTabPages();
TrySelectNewNode(o =>
{
var server = con.Cache.Server;
return server != null && server.Equals(o);
}, false, true, false);
}
Program.Invoke(this, UpdateToolbars);
};
con.Cache.Changed += Cache_Changed;
}
else if (e.Action == CollectionChangeAction.Remove)
{
con.Cache.Changed -= Cache_Changed;
}
RequestRefreshTreeView();
}
private void Cache_Changed(object sender, EventArgs e)
{
RequestRefreshTreeView();
}
private void connection_CachePopulatedOnStartup(object sender, EventArgs e)
{
IXenConnection c = (IXenConnection)sender;
c.CachePopulated -= connection_CachePopulatedOnStartup;
TrySelectNewNode(c, false, true, false);
}
private bool Launched = false;
internal void ProcessCommand(ArgType argType, string[] args)
{
switch (argType)
{
case ArgType.Import:
log.DebugFormat("Importing VM export from {0}", args[0]);
OpenGlobalImportWizard(args[0]);
break;
case ArgType.License:
log.DebugFormat("Installing license from {0}", args[0]);
LaunchLicensePicker(args[0]);
break;
case ArgType.Restore:
log.DebugFormat("Restoring host backup from {0}", args[0]);
new RestoreHostFromBackupCommand(commandInterface, 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(commandInterface, 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.
BringToFront();
Activate();
}
break;
case ArgType.Passwords:
System.Diagnostics.Trace.Assert(false);
break;
}
Launched = true;
}
private void ExpandPanel(UserControl panel)
{
panel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
panel.Location = new Point(0, 0);
panel.Size = new Size(panel.Parent.Width, panel.Parent.Height);
}
// 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;
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.BeforeMajorChange += Connection_BeforeMajorChange;
connection.AfterMajorChange += Connection_AfterMajorChange;
connection.Cache.RegisterCollectionChanged(MessageCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged(PoolCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged(HostCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged(VMCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged(SRCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged(FolderCollectionChangedWithInvoke);
connection.Cache.RegisterCollectionChanged(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.BeforeMajorChange -= Connection_BeforeMajorChange;
connection.AfterMajorChange -= Connection_AfterMajorChange;
connection.Cache.DeregisterCollectionChanged(MessageCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged(PoolCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged(HostCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged(VMCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged(SRCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged(FolderCollectionChangedWithInvoke);
connection.Cache.DeregisterCollectionChanged(TaskCollectionChangedWithInvoke);
connection.CachePopulated -= connection_CachePopulated;
foreach (VM vm in connection.Cache.VMs)
{
this.ConsolePanel.closeVNCForSource(vm);
}
foreach (Host host in connection.Cache.Hosts)
foreach (VM vm in host.Connection.ResolveAll(host.resident_VMs))
if (vm.is_control_domain)
this.ConsolePanel.closeVNCForSource(vm);
connection.EndConnect();
RequestRefreshTreeView();
//CA-41228 refresh submenu items when there are no connections
selectionManager.SetSelection(selectionManager.Selection);
}
// update ui
//XenAdmin.Settings.SaveServerList();
}
catch (Exception exn)
{
log.Error(exn, exn);
// Can't do any more about this.
}
}
///
/// 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.
///
///
///
private void connection_ClearingCache(object sender, EventArgs e)
{
IXenConnection connection = (IXenConnection)sender;
closeActiveWizards(connection);
lock (Alert.XenCenterAlertsLock)
Alert.XenCenterAlerts.RemoveAll(new Predicate(delegate(Alert alert) { return alert.Connection != null && alert.Connection.Equals(connection); }));
}
void connection_CachePopulated(object sender, EventArgs e)
{
IXenConnection connection = sender as IXenConnection;
if (connection == null)
return;
Host master = Helpers.GetMaster(connection);
if (master == null)
return;
log.InfoFormat("Connected to {0} (version {1}, build {2}.{3}) with XenCenter {4} (build {5})",
Helpers.GetName(master), Helpers.HostProductVersionText(master), Helpers.HostProductVersion(master),
Helpers.HostBuildNumber(master), Branding.PRODUCT_VERSION_TEXT, Program.Version.Revision);
// When releasing a new version of the server, we should set xencenter_min and xencenter_max on the server
// as follows:
//
// xencenter_min should be the lowest version of XenCenter we want the new server to work with. In the
// (common) case that we want to force the user to upgrade XenCenter when they upgrade the server,
// xencenter_min should equal the current version of XenCenter. // if (server_min > current_version)
//
// xencenter_max should always equal the current version of XenCenter. This ensures that even if they are
// not required to upgrade, we at least warn them. // else if (server_max > current_version)
int server_min = master.XenCenterMin;
int server_max = master.XenCenterMax;
if (server_min > 0 && server_max > 0)
{
int current_version = (int)API_Version.LATEST;
if (server_min > current_version)
{
connection.EndConnect();
//Program.Invoke(Program.MainWindow, RefreshTreeView);
Program.Invoke(Program.MainWindow, delegate()
{
string url = "https://" + connection.Hostname;
string message = string.Format(Messages.GUI_OUT_OF_DATE, Helpers.GetName(master), url);
int linkStart = message.Length - url.Length - 1;
int linkLength = url.Length;
string linkUrl = url;
new ThreeButtonDialog(
new ThreeButtonDialog.Details(SystemIcons.Error, message, linkStart, linkLength, linkUrl, Messages.CONNECTION_REFUSED_TITLE)).ShowDialog(this);
new ActionBase(Messages.CONNECTION_REFUSED, message, false, true, Messages.CONNECTION_REFUSED_TITLE);
});
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);
Updates.CheckServerPatches();
Updates.CheckServerVersion();
RequestRefreshTreeView();
}
///
/// Ensures all hosts on the connection are disabled if they are in maintenance mode.
///
///
private void CheckMaintenanceMode(IXenConnection connection)
{
foreach (Host host in connection.Cache.Hosts)
{
CheckMaintenanceMode(host);
}
}
///
/// Ensures the host is disabled if it is in maintenance mode by spawning a new HostAction if necessary.
///
///
private void CheckMaintenanceMode(Host host)
{
if (host.IsLive && host.MaintenanceMode && host.enabled)
{
Program.MainWindow.closeActiveWizards(host);
AllowHistorySwitch = true;
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(object sender, CollectionChangeEventArgs e) where T : XenObject
{
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((IXenObject)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)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)Connection_ConnectionClosed_, sender, e);
gc();
}
private void Connection_ConnectionClosed_(object sender, EventArgs e)
{
Program.AssertOnEventThread();
try
{
UpdateToolbars();
}
catch (Exception exn)
{
log.Error(exn, exn);
// Nothing more we can do with this.
}
}
// called whenever our connection with the Xen server fails (i.e., after we've successfully logged in)
void Connection_ConnectionLost(object sender, EventArgs e)
{
if (Program.Exiting)
return;
Program.Invoke(this, (EventHandler)Connection_ConnectionLost_, sender, e);
RequestRefreshTreeView();
gc();
}
private void Connection_ConnectionLost_(object sender, EventArgs e)
{
Program.AssertOnEventThread();
try
{
IXenConnection connection = (IXenConnection)sender;
closeActiveWizards(connection);
UpdateToolbars();
}
catch (Exception exn)
{
log.Error(exn, exn);
// Can do nothing about this.
}
}
private static void gc()
{
log_gc("Before");
GC.Collect();
log_gc("After");
}
private static void log_gc(string when)
{
log.DebugFormat("{0} GC: approx {1} bytes in use", when, GC.GetTotalMemory(false));
for (int i = 0; i <= GC.MaxGeneration; i++)
{
log.DebugFormat("Number of times GC has occurred for generation {0} objects: {1}", i, GC.CollectionCount(i));
}
log.Debug("GDI objects in use: " + Win32.GetGuiResourcesGDICount(Process.GetCurrentProcess().Handle));
log.Debug("USER objects in use: " + Win32.GetGuiResourcesUserCount(Process.GetCurrentProcess().Handle));
}
void connection_ConnectionReconnecting(object sender, EventArgs e)
{
if (Program.Exiting)
return;
RequestRefreshTreeView();
gc();
}
private List hostsInInvalidState = new List();
// 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();
}
void Connection_BeforeMajorChange(object sender, ConnectionMajorChangeEventArgs e)
{
if (e.Background)
Connection_BeforeBackgroundMajorChange();
else
Program.Invoke(this, (EventHandler)Connection_BeforeMajorChange_, sender, e);
}
private void Connection_BeforeMajorChange_(object sender, EventArgs eventArgs)
{
Program.AssertOnEventThread();
try
{
if (inMajorChange)
return;
inMajorChange = true;
SuspendRefreshTreeView();
SuspendUpdateToolbars();
}
catch (Exception exn)
{
log.Error(exn, exn);
// Can do nothing more about this.
}
}
private void Connection_BeforeBackgroundMajorChange()
{
Program.AssertOffEventThread();
try
{
Program.Invoke(this, delegate()
{
SuspendRefreshTreeView();
SuspendUpdateToolbars();
});
}
catch (Exception exn)
{
log.Error(exn, exn);
// Can do nothing more about this.
}
}
void Connection_AfterMajorChange(object sender, ConnectionMajorChangeEventArgs e)
{
if (e.Background)
Connection_AfterBackgroundMajorChange();
else
Program.Invoke(this, (EventHandler)Connection_AfterMajorChange_, sender, e);
}
private void Connection_AfterMajorChange_(object sender, EventArgs eventArgs)
{
Program.AssertOnEventThread();
try
{
try
{
ResumeUpdateToolbars();
}
finally
{
ResumeRefreshTreeView();
}
inMajorChange = false;
}
catch (Exception exn)
{
log.Error(exn, exn);
// Can do nothing more about this.
}
}
private void Connection_AfterBackgroundMajorChange()
{
Program.AssertOffEventThread();
try
{
Program.Invoke(this, delegate()
{
try
{
ResumeUpdateToolbars();
}
finally
{
ResumeRefreshTreeView();
}
});
}
catch (Exception exn)
{
log.Error(exn, exn);
// Can do nothing more about this.
}
}
private bool inMajorChange = false;
public void MajorChange(MethodInvoker f)
{
Program.AssertOnEventThread();
if (inMajorChange)
{
f();
return;
}
inMajorChange = true;
SuspendRefreshTreeView();
SuspendUpdateToolbars();
try
{
f();
}
catch (Exception e)
{
log.Debug("Exception thrown by target of MajorChange.", e);
log.Debug(e, e);
throw;
}
finally
{
try
{
ResumeUpdateToolbars();
}
finally
{
ResumeRefreshTreeView();
}
inMajorChange = false;
}
}
private static Pool PoolAncestorOfNode(VirtualTreeNode node)
{
while (node != null)
{
var pool = node.Tag as Pool;
if (pool != null)
return pool;
node = node.Parent;
}
return null;
}
private static Host HostAncestorOfNode(VirtualTreeNode node)
{
while (node != null)
{
var host = node.Tag as Host;
if (host != null)
return host;
node = node.Parent;
}
return null;
}
private int ignoreRefreshTreeView = 0;
private bool calledRefreshTreeView = false;
private int ignoreUpdateToolbars = 0;
private bool calledUpdateToolbars = false;
void SuspendRefreshTreeView()
{
Program.AssertOnEventThread();
if (ignoreRefreshTreeView == 0)
{
calledRefreshTreeView = false;
}
ignoreRefreshTreeView++;
}
void ResumeRefreshTreeView()
{
Program.AssertOnEventThread();
ignoreRefreshTreeView--;
if (ignoreRefreshTreeView == 0 && calledRefreshTreeView)
{
RequestRefreshTreeView();
}
}
void SuspendUpdateToolbars()
{
Program.AssertOnEventThread();
if (ignoreUpdateToolbars == 0)
{
calledUpdateToolbars = false;
}
ignoreUpdateToolbars++;
}
void ResumeUpdateToolbars()
{
Program.AssertOnEventThread();
ignoreUpdateToolbars--;
if (ignoreUpdateToolbars == 0 && calledUpdateToolbars)
{
UpdateToolbars();
}
}
private void treeViewUpdateManager_Update(object sender, EventArgs e)
{
Program.AssertOffEventThread();
RefreshTreeView();
UpdateHeaderAndTabPages();
}
///
/// Requests a refresh of the main tree view. The refresh will be managed such that we are not overloaded using an UpdateManager.
///
public void RequestRefreshTreeView()
{
if (!Program.Exiting)
{
treeViewUpdateManager.RequestUpdate();
}
}
///
/// Refreshes the main tree view. A new node tree is built on the calling thread and then merged into the main tree view
/// on the main program thread.
///
private void RefreshTreeView()
{
VirtualTreeNode newRootNode = treeBuilder.CreateNewRootNode(TreeSearchBox.Search.AddFullTextFilter(searchTextBox.Text), TreeSearchBoxMode);
Program.Invoke(this, () =>
{
if (ignoreRefreshTreeView > 0)
{
calledRefreshTreeView = true;
return;
}
ignoreRefreshTreeView++; // Some events can be ignored while rebuilding the tree
try
{
treeBuilder.RefreshTreeView(newRootNode, searchTextBox.Text, TreeSearchBoxMode);
}
catch (Exception exn)
{
log.Error(exn, exn);
#if DEBUG
if (Debugger.IsAttached)
throw;
#endif
}
finally
{
ignoreRefreshTreeView--;
}
});
}
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.SetSelection(selectionManager.Selection);
}
}
}
///
/// Must be called on the event thread.
///
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;
///
/// Updates the toolbar buttons. Also updates which tabs are visible.
///
public void UpdateToolbarsCore()
{
// refresh the selection-manager
selectionManager.SetSelection(selectionManager.Selection);
ToolStrip.Height = ToolbarsEnabled ? TOOLBAR_HEIGHT : 0;
ToolStrip.Enabled = ToolbarsEnabled;
MenuBarToolStrip.Visible = MenuBarToolStrip.Enabled = !ToolbarsEnabled;
ShowToolbarMenuItem.Checked = toolbarToolStripMenuItem.Checked = ToolbarsEnabled;
powerOnHostToolStripButton.Available = powerOnHostToolStripButton.Enabled;
startVMToolStripButton.Available = startVMToolStripButton.Enabled;
shutDownToolStripButton.Available = shutDownToolStripButton.Enabled || (!startVMToolStripButton.Available && !powerOnHostToolStripButton.Available);
resumeToolStripButton.Available = resumeToolStripButton.Enabled;
SuspendToolbarButton.Available = SuspendToolbarButton.Enabled || !resumeToolStripButton.Available;
ForceRebootToolbarButton.Available = ((ForceVMRebootCommand)ForceRebootToolbarButton.Command).ShowOnMainToolBar;
ForceShutdownToolbarButton.Available = ((ForceVMShutDownCommand)ForceShutdownToolbarButton.Command).ShowOnMainToolBar;
IXenConnection selectionConnection = selectionManager.Selection.GetConnectionOfFirstItem();
Pool selectionPool = selectionConnection == null ? null : Helpers.GetPool(selectionConnection);
Host selectionMaster = null == selectionPool ? null : selectionPool.Connection.Resolve(selectionPool.master);
// 'Home' tab is only visible if the 'Overview' tree node is selected, or if the tree is
// empty (i.e. at startup).
bool show_home = selectionManager.Selection.Count == 1 && selectionManager.Selection[0].Value == null;
// Only show the HA tab if the host's license has the HA flag set
bool has_ha_license_flag = selectionMaster != null && !selectionMaster.RestrictHAOrlando;
bool george_or_greater = Helpers.GeorgeOrGreater(selectionConnection);
bool mr_or_greater = Helpers.MidnightRideOrGreater(selectionConnection);
// The upsell pages use the first selected XenObject: but they're only shown if there is only one selected object (see calls to ShowTab() below).
bool dmc_upsell = Helpers.FeatureForbidden(selectionManager.Selection.FirstAsXenObject, Host.RestrictDMC);
bool ha_upsell = Helpers.FeatureForbidden(selectionManager.Selection.FirstAsXenObject, Host.RestrictHAFloodgate);
bool wlb_upsell = Helpers.FeatureForbidden(selectionManager.Selection.FirstAsXenObject, Host.RestrictWLB);
bool is_connected = selectionConnection != null && selectionConnection.IsConnected;
bool multi = SelectionManager.Selection.Count > 1;
bool isPoolSelected = selectionManager.Selection.FirstIsPool;
bool isVMSelected = selectionManager.Selection.FirstIsVM;
bool isHostSelected = selectionManager.Selection.FirstIsHost;
bool isSRSelected = selectionManager.Selection.FirstIsSR;
bool isRealVMSelected = selectionManager.Selection.FirstIsRealVM;
bool isTemplateSelected = selectionManager.Selection.FirstIsTemplate;
bool isHostLive = selectionManager.Selection.FirstIsLiveHost;
bool isStorageLinkSelected = selectionManager.Selection.FirstIsStorageLink;
bool isStorageLinkSRSelected = selectionManager.Selection.First is StorageLinkRepository && ((StorageLinkRepository)selectionManager.Selection.First).SR(ConnectionsManager.XenConnectionsCopy) != null;
bool selectedTemplateHasProvisionXML = selectionManager.Selection.FirstIsTemplate && ((VM)selectionManager.Selection[0].XenObject).HasProvisionXML;
NewTabCount = 0;
ShowTab(TabPageHome, !SearchMode && show_home);
ShowTab(TabPageSearch, (SearchMode || show_home || SearchTabVisible));
ShowTab(TabPageGeneral, !multi && !SearchMode && (isVMSelected || (isHostSelected && (isHostLive || !is_connected)) || isPoolSelected || isSRSelected || isStorageLinkSelected));
ShowTab(dmc_upsell ? TabPageBallooningUpsell : TabPageBallooning, !multi && !SearchMode && mr_or_greater && (isVMSelected || (isHostSelected && isHostLive) || isPoolSelected));
ShowTab(TabPageStorage, !multi && !SearchMode && (isRealVMSelected || (isTemplateSelected && !selectedTemplateHasProvisionXML)));
ShowTab(TabPageSR, !multi && !SearchMode && (isSRSelected || isStorageLinkSRSelected));
ShowTab(TabPagePhysicalStorage, !multi && !SearchMode && ((isHostSelected && isHostLive) || isPoolSelected));
ShowTab(TabPageNetwork, !multi && !SearchMode && (isVMSelected || (isHostSelected && isHostLive) || isPoolSelected));
ShowTab(TabPageNICs, !multi && !SearchMode && ((isHostSelected && isHostLive)));
pluginManager.SetSelectedXenObject(selectionManager.Selection.FirstAsXenObject);
bool shownConsoleReplacement = false;
foreach (TabPageFeature f in pluginManager.GetAllFeatures(f => f.IsConsoleReplacement && !f.IsError && !multi && f.ShowTab))
{
ShowTab(f.TabPage, true);
shownConsoleReplacement = true;
}
ShowTab(TabPageConsole, !shownConsoleReplacement && !multi && !SearchMode && (isRealVMSelected || (isHostSelected && isHostLive)));
ShowTab(TabPagePeformance, !multi && !SearchMode && (isRealVMSelected || (isHostSelected && isHostLive)));
ShowTab(ha_upsell ? TabPageHAUpsell : TabPageHA, !multi && !SearchMode && isPoolSelected && has_ha_license_flag);
ShowTab(TabPageSnapshots, !multi && !SearchMode && george_or_greater && isRealVMSelected);
//Disable the WLB tab from Clearwater onwards
if(selectionManager.Selection.All(s=>!Helpers.ClearwaterOrGreater(s.Connection)))
ShowTab(wlb_upsell ? TabPageWLBUpsell : TabPageWLB, !multi && !SearchMode && isPoolSelected && george_or_greater);
ShowTab(TabPageAD, !multi && !SearchMode && (isPoolSelected || isHostSelected && isHostLive) && george_or_greater);
// put plugin tabs before logs tab
foreach (TabPageFeature f in pluginManager.GetAllFeatures(f => !f.IsConsoleReplacement && !multi && f.ShowTab))
{
ShowTab(f.TabPage, true);
}
ShowTab(TabPageHistory, !SearchMode);
// 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.
MajorChange(() =>
{
bool treeViewHasFocus = treeView.ContainsFocus;
searchTextBox.SaveState();
ChangeToNewTabs();
if (!searchMode && treeViewHasFocus)
treeView.Focus();
searchTextBox.RestoreState();
});
}
private bool SearchTabVisible
{
get
{
if (selectionManager.Selection.Count == 1)
{
Host host = selectionManager.Selection[0].XenObject as Host;
if (host != null)
{
return host.IsLive;
}
if (selectionManager.Selection[0].XenObject is Pool)
{
return true;
}
if (selectionManager.Selection[0].GroupingTag != null)
{
return true;
}
if (selectionManager.Selection[0].XenObject is Folder)
{
return true;
}
}
else if (selectionManager.Selection.Count > 1)
{
return true;
}
return false;
}
}
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 (NewTabsContains(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;
if (s != null && s.InError && AllowHistorySwitch)
{
s.InError = false;
return TabPageHistory;
}
else
{
TabPage last_selected_page = GetLastSelectedPage(o);
return
last_selected_page != null && NewTabsContains(last_selected_page) ?
last_selected_page :
NewTabs[0];
}
}
public 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 bool NewTabsContains(TabPage tp)
{
return NewTabsIndexOf(tp) != -1;
}
private int NewTabsIndexOf(TabPage tp)
{
for (int i = 0; i < NewTabCount; i++)
{
if (NewTabs[i] == tp)
return i;
}
return -1;
}
private void topLevelMenu_DropDownOpening(object sender, EventArgs e)
{
ToolStripMenuItem menu = (ToolStripMenuItem)sender;
//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[i] is ToolStripSeparator)
{
menu.DropDownItems.RemoveAt(i);
}
}
}
// get insert index using the placeholder
int insertIndex = pluginMenuItemStartIndexes[menu];
bool addSeparatorAtEnd = 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)
{
MenuItemFeature menuItemFeature = feature as MenuItemFeature;
if (menuItemFeature != null && menuItemFeature.ParentFeature == null && (int)menuItemFeature.Menu == MainMenuBar.Items.IndexOf(menu))
{
Command cmd = menuItemFeature.GetCommand(commandInterface, SelectionManager.Selection);
menu.DropDownItems.Insert(insertIndex, new CommandToolStripMenuItem(cmd));
insertIndex++;
addSeparatorAtEnd = true;
}
ParentMenuItemFeature parentMenuItemFeature = feature as ParentMenuItemFeature;
if (parentMenuItemFeature != null && (int)parentMenuItemFeature.Menu == MainMenuBar.Items.IndexOf(menu))
{
Command cmd = parentMenuItemFeature.GetCommand(commandInterface, SelectionManager.Selection);
CommandToolStripMenuItem parentMenuItem = new CommandToolStripMenuItem(cmd);
menu.DropDownItems.Insert(insertIndex, parentMenuItem);
insertIndex++;
addSeparatorAtEnd = true;
foreach (MenuItemFeature childFeature in parentMenuItemFeature.Features)
{
Command childCommand = childFeature.GetCommand(commandInterface, SelectionManager.Selection);
parentMenuItem.DropDownItems.Add(new CommandToolStripMenuItem(childCommand));
}
}
}
}
if (addSeparatorAtEnd)
{
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;
Host best_host = hostAncestor ?? (connection == null ? null : Helpers.GetMaster(connection));
bool george_or_greater = best_host != null && Helpers.GeorgeOrGreater(best_host);
exportSettingsToolStripMenuItem.Enabled = ConnectionsManager.XenConnectionsCopy.Count > 0;
this.MenuShortcuts = true;
startOnHostToolStripMenuItem.Available = startOnHostToolStripMenuItem.Enabled;
resumeOnToolStripMenuItem.Available = resumeOnToolStripMenuItem.Enabled;
relocateToolStripMenuItem.Available = relocateToolStripMenuItem.Enabled;
storageLinkToolStripMenuItem.Available = storageLinkToolStripMenuItem.Enabled;
sendCtrlAltDelToolStripMenuItem.Enabled = (TheTabControl.SelectedTab == TabPageConsole) && vm && ((VM)selectionManager.Selection.First).power_state == vm_power_state.Running;
templatesToolStripMenuItem1.Checked = Properties.Settings.Default.DefaultTemplatesVisible;
customTemplatesToolStripMenuItem.Checked = Properties.Settings.Default.UserTemplatesVisible;
localStorageToolStripMenuItem.Checked = Properties.Settings.Default.LocalSRsVisible;
ShowHiddenObjectsToolStripMenuItem.Checked = Properties.Settings.Default.ShowHiddenVMs;
connectDisconnectToolStripMenuItem.Enabled = ConnectionsManager.XenConnectionsCopy.Count > 0;
checkForUpdatesToolStripMenuItem.Available = !Helpers.CommonCriteriaCertificationRelease;
}
// Which XenObject's are selectable in the tree, and draggable in the search results?
public static bool IsSelectableXenModelObject(IXenObject o)
{
return o != null;
}
public static bool CanSelectNode(VirtualTreeNode node)
{
if (node.Tag == null) // XenCenter node
return true;
if (node.Tag is IXenObject)
return IsSelectableXenModelObject(node.Tag as IXenObject);
if (node.Tag is GroupingTag)
return true;
return false;
}
private void TreeView_BeforeSelect(object sender, VirtualTreeViewCancelEventArgs e)
{
if (e.Node == null)
return;
if (!CanSelectNode(e.Node))
{
e.Cancel = true;
return;
}
SearchMode = false;
AllowHistorySwitch = false;
}
private void treeView_SelectionsChanged(object sender, EventArgs e)
{
// this is fired when the selection of the main treeview changes.
List items = new List();
foreach (VirtualTreeNode node in treeView.SelectedNodes)
{
GroupingTag groupingTag = node.Tag as GroupingTag;
IXenObject xenObject = node.Tag as IXenObject;
if (xenObject != null)
{
items.Add(new SelectedItem(xenObject, xenObject.Connection, HostAncestorOfNode(node), PoolAncestorOfNode(node)));
}
else
{
items.Add(new SelectedItem(groupingTag));
}
}
// setting this sets the XenCenter selection. Everything that needs to know about the selection and
// selection changes should use this object.
selectionManager.SetSelection(items);
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(sender, EventArgs.Empty);
if (TheTabControl.SelectedTab != null)
TheTabControl.SelectedTab.Refresh();
UpdateHeader();
}
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));
}
///
/// Apply license, if HostAncestorOfSelectedNode is null, show host picker, if filepath == "" show filepicker
///
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)
{
AllowHistorySwitch = true;
ApplyLicenseAction action = new ApplyLicenseAction(host.Connection, host, filePath);
ActionProgressDialog actionProgress = new ActionProgressDialog(action, ProgressBarStyle.Marquee);
actionProgress.Text = Messages.INSTALL_LICENSE_KEY;
actionProgress.ShowDialog(this);
}
private void dialog_HelpRequest(object sender, EventArgs e)
{
Help.HelpManager.Launch("LicenseKeyDialog");
}
private void TreeView_NodeMouseClick(object sender, VirtualTreeNodeMouseClickEventArgs e)
{
try
{
TreeView_NodeMouseClick_(sender, e);
if (SearchMode)
{
SearchMode = false;
TheTabControl_SelectedIndexChanged(null, null);
UpdateHeader();
}
}
catch (Exception exn)
{
log.Error(exn, exn);
// Swallow this exception -- there's no point throwing it on.
#if DEBUG
throw;
#endif
}
}
private void TreeView_NodeMouseClick_(object sender, VirtualTreeNodeMouseClickEventArgs e)
{
if (treeView.Nodes.Count < 1)
return;
if (e.Button != MouseButtons.Right)
return;
// Handle r-click menu stuff
if (treeView.SelectedNodes.Count == 0)
{
treeView.SelectedNode = e.Node;
if (treeView.SelectedNode != e.Node) // if node is unselectable in TreeView_BeforeSelect: CA-26615
{
return;
}
}
else if (treeView.SelectedNodes.Contains(e.Node))
{
// don't change the selection - just show the menu.
}
else if (CanSelectNode(e.Node))
{
treeView.SelectedNode = e.Node;
}
else
{
// can't select node - don't show menu.
return;
}
MainMenuBar_MenuActivate(MainMenuBar, new EventArgs());
TreeContextMenu.SuspendLayout();
TreeContextMenu.Items.Clear();
if (e.Node == treeView.Nodes[0] && treeView.SelectedNodes.Count == 1)
{
// XenCenter (top most)
TreeContextMenu.Items.Add(new CommandToolStripMenuItem(new AddHostCommand(commandInterface), true));
TreeContextMenu.Items.Add(new CommandToolStripMenuItem(new NewPoolCommand(commandInterface, new SelectedItem[0]), true));
TreeContextMenu.Items.Add(new CommandToolStripMenuItem(new ConnectAllHostsCommand(commandInterface), true));
TreeContextMenu.Items.Add(new CommandToolStripMenuItem(new DisconnectAllHostsCommand(commandInterface), true));
}
else
{
TreeContextMenu.Items.AddRange(contextMenuBuilder.Build(SelectionManager.Selection));
}
int insertIndex = TreeContextMenu.Items.Count;
if (TreeContextMenu.Items.Count > 0)
{
CommandToolStripMenuItem lastItem = TreeContextMenu.Items[TreeContextMenu.Items.Count - 1] as CommandToolStripMenuItem;
if (lastItem != null && lastItem.Command is PropertiesCommand)
{
insertIndex--;
}
}
AddExpandCollapseItems(insertIndex, treeView.SelectedNodes, TreeContextMenu);
AddOrgViewItems(insertIndex, treeView.SelectedNodes, TreeContextMenu);
TreeContextMenu.ResumeLayout();
if (TreeContextMenu.Items.Count > 0)
{
TreeContextMenu.Show(treeView, e.Location);
}
}
private void AddExpandCollapseItems(int insertIndex, IList nodes, ContextMenuStrip contextMenuStrip)
{
if (nodes.Count == 1 && nodes[0].Nodes.Count == 0)
{
return;
}
Command cmd = new CollapseChildTreeNodesCommand(commandInterface, nodes);
if (cmd.CanExecute())
{
contextMenuStrip.Items.Insert(insertIndex, new CommandToolStripMenuItem(cmd, true));
}
cmd = new ExpandTreeNodesCommand(commandInterface, nodes);
if (cmd.CanExecute())
{
contextMenuStrip.Items.Insert(insertIndex, new CommandToolStripMenuItem(cmd, true));
}
}
private void AddOrgViewItems(int insertIndex, IList nodes, ContextMenuStrip contextMenuStrip)
{
if ((TreeSearchBoxMode != XenAdmin.Controls.TreeSearchBox.Mode.Objects
&& TreeSearchBoxMode != XenAdmin.Controls.TreeSearchBox.Mode.Organization)
|| nodes.Count == 0)
{
return;
}
Command cmd = new RemoveFromFolderCommand(commandInterface, nodes);
if (cmd.CanExecute())
{
contextMenuStrip.Items.Insert(insertIndex, new CommandToolStripMenuItem(cmd, true));
}
cmd = new UntagCommand(commandInterface, nodes);
if (cmd.CanExecute())
{
contextMenuStrip.Items.Insert(insertIndex, new CommandToolStripMenuItem(cmd, true));
}
}
///
/// 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.
///
private void TheTabControl_SelectedIndexChanged(object sender, EventArgs e)
{
AllowHistorySwitch = false;
if (IgnoreTabChanges)
return;
TabPage t = TheTabControl.SelectedTab;
if (!SearchMode)
{
History.NewHistoryItem(new XenModelObjectHistoryItem(selectionManager.Selection.FirstAsXenObject, t));
}
if (t != TabPageBallooning)
{
BallooningPage.IsHidden();
}
if (t == TabPageConsole)
{
if (selectionManager.Selection.FirstIsRealVM)
{
ConsolePanel.setCurrentSource((VM)selectionManager.Selection.First);
UnpauseVNC(e != null && sender == TheTabControl);
}
else if (selectionManager.Selection.FirstIsHost)
{
ConsolePanel.setCurrentSource((Host)selectionManager.Selection.First);
UnpauseVNC(e != null && sender == TheTabControl);
}
}
else
{
ConsolePanel.PauseAllViews();
if (t == TabPageGeneral)
{
GeneralPage.XenObject = selectionManager.Selection.FirstAsXenObject;
}
else if (t == TabPageBallooning)
{
BallooningPage.XenObject = selectionManager.Selection.FirstAsXenObject;
}
else if (t == TabPageSR)
{
StorageLinkRepository slr = selectionManager.Selection.First as StorageLinkRepository;
SrStoragePage.SR = slr == null ? selectionManager.Selection.First as SR : slr.SR(ConnectionsManager.XenConnectionsCopy);
}
else if (t == TabPageNetwork)
{
NetworkPage.XenObject = selectionManager.Selection.FirstAsXenObject;
}
else if (t == TabPageHistory)
{
// Unmark node if user has now seen error in log tab
if (selectionManager.Selection.FirstAsXenObject != null)
{
selectionManager.Selection.FirstAsXenObject.InError = false;
}
HistoryPage.RefreshDisplayedEvents();
RequestRefreshTreeView();
}
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)
{
if (selectionManager.Selection.First is GroupingTag)
{
GroupingTag gt = (GroupingTag)selectionManager.Selection.First;
SearchPage.Search = Search.SearchForGroup(gt.Grouping, gt.Parent, gt.Group);
}
else
{
SearchPage.XenObject = selectionManager.Selection.Count > 1 ? null : selectionManager.Selection.FirstAsXenObject;
}
}
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, commandInterface);
PhysicalStoragePage.Host = selectionManager.Selection.First as Host;
PhysicalStoragePage.Connection = selectionManager.Selection.GetConnectionOfFirstItem();
}
else if (t == TabPageAD)
{
AdPage.XenObject = selectionManager.Selection.FirstAsXenObject;
}
}
if (t == TabPagePeformance)
{
PerformancePage.ResumeGraph();
}
else
{
PerformancePage.PauseGraph();
}
if (t == TabPageSearch)
{
SearchPage.PanelShown();
}
else
{
SearchPage.PanelHidden();
}
if (t != null)
{
SetLastSelectedPage(selectionManager.Selection.First, t);
}
}
private void UnpauseVNC(bool focus)
{
ConsolePanel.UnpauseActiveView();
if (focus)
{
ConsolePanel.FocusActiveView();
ConsolePanel.SwitchIfRequired();
}
}
///
/// The tabs that may be visible in the main GUI window. Used in SwitchToTab().
///
public enum Tab
{
Overview, Home, Settings, Storage, Network, Console, Performance, History, NICs, SR
}
public void SwitchToTab(Tab tab)
{
switch (tab)
{
case Tab.Overview:
TheTabControl.SelectedTab = TabPageSearch;
break;
case Tab.Home:
TheTabControl.SelectedTab = TabPageHome;
break;
case Tab.Settings:
TheTabControl.SelectedTab = TabPageGeneral;
break;
case Tab.Storage:
TheTabControl.SelectedTab = TabPageStorage;
break;
case Tab.Network:
TheTabControl.SelectedTab = TabPageNetwork;
break;
case Tab.Console:
TheTabControl.SelectedTab = TabPageConsole;
break;
case Tab.Performance:
TheTabControl.SelectedTab = TabPagePeformance;
break;
case Tab.History:
TheTabControl.SelectedTab = TabPageHistory;
break;
case Tab.NICs:
TheTabControl.SelectedTab = TabPageNICs;
break;
case Tab.SR:
TheTabControl.SelectedTab = TabPageSR;
break;
default:
throw new NotImplementedException();
}
}
#region "Window" main menu item
private void windowToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
{
windowToolStripMenuItem.DropDown.Items.Clear();
topLevelMenu_DropDownOpening(sender, e);
foreach (Form form in GetAuxWindows())
{
ToolStripMenuItem item = NewToolStripMenuItem(form.Text.EscapeAmpersands());
item.Tag = form;
windowToolStripMenuItem.DropDown.Items.Add(item);
}
}
private List