/* 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.Windows.Forms; using XenAdmin.Commands; using XenAdmin.Network; using XenAdmin.Core; using XenAPI; using XenAdmin.Controls; using XenAdmin.Actions; namespace XenAdmin.Dialogs { public partial class NewPoolDialog : XenDialogBase { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private readonly List connections = new List(); private List newConnections = new List(); public NewPoolDialog(Host host) { InitializeComponent(); customTreeViewServers.NodeIndent = 3; customTreeViewServers.ShowCheckboxes = true; customTreeViewServers.ShowDescription = true; customTreeViewServers.ShowImages = false; getAllCurrentConnections(); ConnectionsManager.XenConnections.CollectionChanged += XenConnections_CollectionChanged; poolName = string.Empty; //forcing user to enter something before the Next button is enabled selectMaster(host); updateButtons(); } public NewPoolDialog(List hosts) : this(hosts.Count >= 1 ? hosts[0] : null) { selectSlaves(hosts); } private enum InvalidReasons { NONE, EMPTY_POOL_NAME, NO_MASTER, MAX_POOL_SIZE_EXCEEDED }; private InvalidReasons validToClose { get { if (poolName == "") return InvalidReasons.EMPTY_POOL_NAME; if (comboBoxServers.SelectedIndex < 0) return InvalidReasons.NO_MASTER; if (comboBoxServers.Items.Count <= 0) return InvalidReasons.NO_MASTER; Host master = getMaster(); if (master != null) { List slaves = getSlaves(); if (PoolJoinRules.WillExceedPoolMaxSize(master.Connection, slaves.Count)) return InvalidReasons.MAX_POOL_SIZE_EXCEEDED; } return InvalidReasons.NONE; } } private string poolName { get { return textBoxName.Text.Trim(); } set { textBoxName.Text = value; } } private string poolDescription { get { return textBoxDescription.Text; } } private void updateButtons() { switch (validToClose) { case InvalidReasons.NONE: buttonCreate.Enabled = true; toolTipContainerCreate.RemoveAll(); break; case InvalidReasons.NO_MASTER: buttonCreate.Enabled = false; toolTipContainerCreate.SetToolTip(Messages.NO_ELIGIBLE_MASTER); break; case InvalidReasons.EMPTY_POOL_NAME: buttonCreate.Enabled = false; toolTipContainerCreate.SetToolTip(Messages.POOL_NAME_EMPTY); break; case InvalidReasons.MAX_POOL_SIZE_EXCEEDED: buttonCreate.Enabled = false; toolTipContainerCreate.SetToolTip(Messages.NEWPOOL_WILL_EXCEED_POOL_MAX_SIZE); break; } } private void createPool() { try { Host master = getMaster(); if (master == null) { log.Error("Disconnected during create pool"); return; } List slaves = getSlaves(); // Check supp packs and warn List badSuppPacks = PoolJoinRules.HomogeneousSuppPacksDiffering(slaves, master); if (!HelpersGUI.GetPermissionFor(badSuppPacks, sp => true, Messages.NEW_POOL_SUPP_PACK, Messages.NEW_POOL_SUPP_PACKS, false, "PoolJoinSuppPacks")) { return; } // Are there any hosts which are forbidden from masking their CPUs for licensing reasons? // If so, we need to show upsell. if (null != slaves.Find(host => !PoolJoinRules.CompatibleCPUs(host, master, false) && Helpers.FeatureForbidden(host, Host.RestrictCpuMasking) && !PoolJoinRules.FreeHostPaidMaster(host, master, false))) // in this case we can upgrade the license and then mask the CPU { using (var dlg = new UpsellDialog(HiddenFeatures.LinkLabelHidden ? Messages.UPSELL_BLURB_CPUMASKING : Messages.UPSELL_BLURB_CPUMASKING + Messages.UPSELL_BLURB_TRIAL, InvisibleMessages.UPSELL_LEARNMOREURL_TRIAL)) dlg.ShowDialog(this); return; } if (!HelpersGUI.GetPermissionFor(slaves, host => PoolJoinRules.FreeHostPaidMaster(host, master, false), Messages.NEW_POOL_LICENSE_MESSAGE, Messages.NEW_POOL_LICENSE_MESSAGE_MULTIPLE, true, "PoolJoinRelicensing") || !HelpersGUI.GetPermissionFor(slaves, host => !PoolJoinRules.CompatibleCPUs(host, master, false), Messages.NEW_POOL_CPU_MASKING_MESSAGE, Messages.NEW_POOL_CPU_MASKING_MESSAGE_MULTIPLE, true, "PoolJoinCpuMasking") || !HelpersGUI.GetPermissionFor(slaves, host => !PoolJoinRules.CompatibleAdConfig(host, master, false), Messages.NEW_POOL_AD_MESSAGE, Messages.NEW_POOL_AD_MESSAGE_MULTIPLE, true, "PoolJoinAdConfiguring") || !HelpersGUI.GetPermissionForCpuFeatureLevelling(slaves, Helpers.GetPoolOfOne(master.Connection))) { return; } log.DebugFormat("Creating new pool {0} ({1}) with master {2}", poolName, poolDescription, Helpers.GetName(master)); foreach (Host slave in slaves) log.DebugFormat(" Slave {0}", Helpers.GetName(slave)); new CreatePoolAction(master, slaves, poolName, poolDescription, AddHostToPoolCommand.GetAdPrompt, AddHostToPoolCommand.NtolDialog, ApplyLicenseEditionCommand.ShowLicensingFailureDialog).RunAsync(); } catch (System.Net.WebException exn) { log.Debug(exn, exn); } } private void addConnectionsToListBox() { ConnectionWrapperWithMoreStuff master = (comboBoxServers.Items.Count > 0 ? (ConnectionWrapperWithMoreStuff)comboBoxServers.SelectedItem : null); foreach (ConnectionWrapperWithMoreStuff c in connections) { c.TheMaster = master; c.Refresh(); } customTreeViewServers.BeginUpdate(); try { customTreeViewServers.ClearAllNodes(); foreach (ConnectionWrapperWithMoreStuff c in connections) { customTreeViewServers.AddNode(c); } customTreeViewServers.Resort(); } finally { customTreeViewServers.EndUpdate(); customTreeViewServers.Invalidate(); } } private void getAllCurrentConnections() { List toRemove = new List(); foreach (IXenConnection connection in ConnectionsManager.XenConnectionsCopy) { if (connection != null&&connection.IsConnected) { bool contains = false; foreach (ConnectionWrapperWithMoreStuff cw in connections) { if (cw.Connection == connection) { contains = true; break; } } if (!contains) { ConnectionWrapperWithMoreStuff c = new ConnectionWrapperWithMoreStuff(connection); connections.Add(c); connection.CachePopulated -= Connection_CachePopulated; connection.CachePopulated += Connection_CachePopulated; connection.ConnectionStateChanged -= connection_ConnectionStateChanged; connection.ConnectionStateChanged += connection_ConnectionStateChanged; if (newConnections.Contains(connection)) { c.State = CheckState.Checked; toRemove.Add(connection); } } } } foreach (IXenConnection connection in toRemove) { newConnections.Remove(connection); } connections.RemoveAll((Predicate)delegate(ConnectionWrapperWithMoreStuff c) { return !ConnectionsManager.XenConnectionsCopy.Contains(c.Connection); }); addConnectionsToComboBox(); } private void addConnectionsToComboBox() { comboBoxServers.Items.Clear(); ConnectionWrapperWithMoreStuff master = null; foreach (ConnectionWrapperWithMoreStuff wrappedConnection in connections) { wrappedConnection.Refresh(); if (wrappedConnection.CanBeMaster) { comboBoxServers.Items.Add(wrappedConnection); if (wrappedConnection.WillBeMaster) master = wrappedConnection; } } if (master != null) { comboBoxServers.SelectedItem = master; } else if (comboBoxServers.Items.Count > 0) { ConnectionWrapperWithMoreStuff defaultMaster = comboBoxServers.Items[0] as ConnectionWrapperWithMoreStuff; setAsMaster(defaultMaster); comboBoxServers.SelectedItem = defaultMaster; } addConnectionsToListBox(); updateButtons(); } private void selectMaster(Host master) { if (master == null || Helpers.GetPool(master.Connection) != null) return; foreach (ConnectionWrapperWithMoreStuff c in connections) { if (c.WillBeMaster) c.State = CheckState.Unchecked; } foreach (ConnectionWrapperWithMoreStuff c in connections) { if (c.Connection == master.Connection) { if (c.CanBeMaster) setAsMaster(c); return; } } } private void selectSlaves(List slaves) { foreach (ConnectionWrapperWithMoreStuff c in connections) { if (c.AllowedAsSlave && slaves.Find(slave => (c.Connection == slave.Connection)) != null) // one of the slaves is the connection we're looking at { c.State = CheckState.Checked; } } } private void setAsMaster(ConnectionWrapperWithMoreStuff defaultMaster) { foreach (ConnectionWrapperWithMoreStuff connectionWrapper in connections) connectionWrapper.TheMaster = defaultMaster; comboBoxServers.SelectedItem = defaultMaster; addConnectionsToListBox(); } private Host getMaster() { ConnectionWrapperWithMoreStuff connectionToMaster = null; foreach (ConnectionWrapperWithMoreStuff connection in connections) { if (connection.WillBeMaster) { connectionToMaster = connection; break; } } return connectionToMaster != null ? Helpers.GetMaster(connectionToMaster.Connection) : null; } private List getSlaves() { List slaveConnections = new List(); foreach (ConnectionWrapperWithMoreStuff connection in connections) { if (connection.State == CheckState.Checked && !connection.WillBeMaster && connection.AllowedAsSlave) { slaveConnections.Add(Helpers.GetMaster(connection.Connection)); } } return slaveConnections; } private void comboBoxServers_SelectionChangeCommitted(object sender, EventArgs e) { setAsMaster(comboBoxServers.SelectedItem as ConnectionWrapperWithMoreStuff); } private void buttonAddNewServer_Click(object sender, EventArgs e) { IXenConnection newConnection = new XenConnection(); newConnections.Add(newConnection); new AddServerTask(this, newConnection, null).Start(); } private void comboBoxServers_SelectedIndexChanged(object sender, EventArgs e) { updateButtons(); } private void customTreeViewServers_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyChar != (char)Keys.Space || !(customTreeViewServers.SelectedItem is CustomTreeNode)) return; CustomTreeNode node = (CustomTreeNode)customTreeViewServers.SelectedItem; if (!node.Enabled) return; node.State = node.State == CheckState.Unchecked ? CheckState.Checked : CheckState.Unchecked; customTreeViewServers.Refresh(); } private void textBoxName_TextChanged(object sender, EventArgs e) { updateButtons(); } void XenConnections_CollectionChanged(object sender, CollectionChangeEventArgs e) { Program.BeginInvoke(this, getAllCurrentConnections); } void Connection_CachePopulated(object sender, EventArgs e) { Program.BeginInvoke(this, addConnectionsToComboBox); } void connection_ConnectionStateChanged(object sender, EventArgs e) { Program.BeginInvoke(this, addConnectionsToComboBox); } private void buttonCreate_Click(object sender, EventArgs e) { createPool(); Close(); } private void buttonCancel_Click(object sender, EventArgs e) { Close(); } private void customTreeViewServers_ItemCheckChanged(object sender, EventArgs e) { updateButtons(); } } }