mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-22 16:36:03 +01:00
bd36a85bff
Signed-off-by: Mihaela Stoica <mihaela.stoica@citrix.com>
401 lines
16 KiB
C#
401 lines
16 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.Linq;
|
|
using System.Text;
|
|
|
|
using XenAPI;
|
|
using XenAdmin.Core;
|
|
using XenAdmin.Model;
|
|
using XenAdmin.Network;
|
|
|
|
|
|
namespace XenAdmin.Actions
|
|
{
|
|
public class CreateBondAction : AsyncAction
|
|
{
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
private class NewBond
|
|
{
|
|
internal NewBond(Bond bond, PIF master, List<PIF> slaves)
|
|
{
|
|
this.bond = bond;
|
|
this.master = master;
|
|
this.slaves = slaves;
|
|
}
|
|
|
|
internal Bond bond;
|
|
internal PIF master;
|
|
internal List<PIF> slaves;
|
|
}
|
|
|
|
private readonly string name_label;
|
|
private readonly bool autoplug;
|
|
private readonly long mtu;
|
|
private readonly bond_mode bondMode;
|
|
private readonly Dictionary<Host, List<PIF>> PIFs = new Dictionary<Host, List<PIF>>();
|
|
private readonly bool bostonOrGreater; // in Boston, most network configuration is done automatically by xapi (PR-1006)
|
|
private readonly bool tampaOrGreater;
|
|
private readonly Host Master;
|
|
private readonly Bond.hashing_algoritm hashingAlgoritm;
|
|
|
|
/// <param name="name_label">The name for the new network.</param>
|
|
/// <param name="PIFs_on_master">The PIFs on the master representing the physical NICs that are to be bonded together.</param>
|
|
/// <param name="autoplug">Whether the new network is marked AutoPlug.</param>
|
|
/// <param name="mtu">The MTU for the Bond, ignored for pre cowley</param>
|
|
/// <param name="bondMode">The bond mode, ignored for pre-Boston</param>
|
|
public CreateBondAction(IXenConnection connection, string name_label, List<PIF> PIFs_on_master, bool autoplug, long mtu, bond_mode bondMode,
|
|
Bond.hashing_algoritm hashingAlgoritm)
|
|
: base(connection, string.Format(Messages.ACTION_CREATE_BOND_TITLE, name_label),
|
|
string.Format(Messages.ACTION_CREATE_BOND_DESCRIPTION, name_label))
|
|
{
|
|
this.name_label = name_label;
|
|
this.autoplug = autoplug;
|
|
this.mtu = mtu;
|
|
this.bondMode = bondMode;
|
|
this.hashingAlgoritm = hashingAlgoritm;
|
|
|
|
Pool = Helpers.GetPoolOfOne(Connection);
|
|
if (Pool == null)
|
|
throw new Failure(Failure.INTERNAL_ERROR, "Pool has gone away");
|
|
|
|
Master = Connection.Resolve(Pool.master);
|
|
if (Master == null)
|
|
throw new Failure(Failure.INTERNAL_ERROR, "Pool master has gone away");
|
|
|
|
bostonOrGreater = Helpers.BostonOrGreater(connection);
|
|
tampaOrGreater = Helpers.TampaOrGreater(connection);
|
|
|
|
foreach (Host host in Connection.Cache.Hosts)
|
|
AppliesTo.Add(host.opaque_ref);
|
|
|
|
#region RBAC Dependencies
|
|
ApiMethodsToRoleCheck.Add("host.management_reconfigure");
|
|
ApiMethodsToRoleCheck.Add("network.create");
|
|
ApiMethodsToRoleCheck.Add("network.destroy");
|
|
ApiMethodsToRoleCheck.Add("network.remove_from_other_config");
|
|
ApiMethodsToRoleCheck.Add("pif.reconfigure_ip");
|
|
ApiMethodsToRoleCheck.Add("pif.plug");
|
|
ApiMethodsToRoleCheck.Add("bond.create");
|
|
ApiMethodsToRoleCheck.Add("bond.destroy");
|
|
ApiMethodsToRoleCheck.AddRange(XenAPI.Role.CommonSessionApiList);
|
|
ApiMethodsToRoleCheck.AddRange(XenAPI.Role.CommonTaskApiList);
|
|
#endregion
|
|
|
|
PIFs = NetworkingHelper.PIFsOnAllHosts(PIFs_on_master);
|
|
// these locks will be cleared in clean()
|
|
foreach (List<PIF> pifs in PIFs.Values)
|
|
{
|
|
foreach (PIF pif in pifs)
|
|
{
|
|
pif.Locked = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<NewBond> new_bonds;
|
|
private XenAPI.Network network;
|
|
protected override void Run()
|
|
{
|
|
// the network lock and the connection.expectDisruption will be cleared in clean()
|
|
|
|
new_bonds = new List<NewBond>();
|
|
network = null;
|
|
Connection.ExpectDisruption = true;
|
|
int inc = 100 / (Connection.Cache.HostCount * 3 + 1);
|
|
string network_ref = CreateNetwork(0, inc);
|
|
|
|
try
|
|
{
|
|
network = Connection.WaitForCache(new XenRef<XenAPI.Network>(network_ref));
|
|
network.Locked = true;
|
|
XenAPI.Network.remove_from_other_config(Session, network_ref, XenAPI.Network.CREATE_IN_PROGRESS);
|
|
|
|
int lo = inc;
|
|
foreach (Host host in GetHostsMasterLast())
|
|
{
|
|
List<PIF> pifs = PIFs[host].FindAll(x => x.physical).ToList();
|
|
|
|
List<XenRef<PIF>> pif_refs = new List<XenRef<PIF>>();
|
|
foreach (PIF pif in pifs)
|
|
{
|
|
pif_refs.Add(new XenRef<PIF>(pif.opaque_ref));
|
|
}
|
|
|
|
log.DebugFormat("Creating bond on {0} with {1} PIFs...", Helpers.GetName(host), pifs.Count);
|
|
|
|
Dictionary<string, string> bondProperties = new Dictionary<string, string>();
|
|
if (bondMode == bond_mode.lacp)
|
|
bondProperties.Add("hashing_algorithm", Bond.HashingAlgoritmToString(hashingAlgoritm));
|
|
|
|
RelatedTask = tampaOrGreater ?
|
|
Bond.async_create(Session, network_ref, pif_refs, "", bondMode, bondProperties) :
|
|
bostonOrGreater ?
|
|
Bond.async_create(Session, network_ref, pif_refs, "", bondMode) :
|
|
Bond.async_create(Session, network_ref, pif_refs, "");
|
|
|
|
PollToCompletion(lo, lo + inc);
|
|
lo += inc;
|
|
log.DebugFormat("Creating bond on {0} done: bond is {1}.", Helpers.GetName(host), Result);
|
|
|
|
Bond new_bond = Connection.WaitForCache(new XenRef<Bond>(Result));
|
|
if (new_bond == null)
|
|
throw new Failure(Failure.INTERNAL_ERROR, "Bond didn't appear in our cache!");
|
|
|
|
PIF new_master = Connection.Resolve(new_bond.master);
|
|
if (new_master == null)
|
|
throw new Failure(Failure.INTERNAL_ERROR, "Bond master didn't appear in our cache!");
|
|
|
|
new_bonds.Add(new NewBond(new_bond, new_master, pifs));
|
|
|
|
new_bond.Locked = true;
|
|
new_master.Locked = true;
|
|
}
|
|
|
|
foreach (NewBond new_bond in new_bonds)
|
|
{
|
|
lo += inc;
|
|
ReconfigureManagementInterfaces(new_bond.slaves, new_bond.master, lo);
|
|
}
|
|
|
|
foreach (NewBond new_bond in new_bonds)
|
|
{
|
|
lo += inc;
|
|
if (!bostonOrGreater)
|
|
NetworkingActionHelpers.Plug(this, new_bond.master, lo);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
foreach (NewBond new_bond in new_bonds)
|
|
{
|
|
RevertManagementInterfaces(new_bond);
|
|
DestroyBond(new_bond.bond);
|
|
}
|
|
|
|
DestroyNetwork(network_ref);
|
|
throw;
|
|
}
|
|
|
|
Description = string.Format(Messages.ACTION_CREATE_BOND_DONE, name_label);
|
|
}
|
|
|
|
private List<Host> GetHostsMasterLast()
|
|
{
|
|
List<Host> result = new List<Host>(Connection.Cache.Hosts);
|
|
result.Remove(Master);
|
|
result.Add(Master);
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Nothrow guarantee.
|
|
/// </summary>
|
|
/// <param name="network"></param>
|
|
/// <param name="new_bonds"></param>
|
|
private void UnlockAll(XenAPI.Network network, List<NewBond> new_bonds)
|
|
{
|
|
if (network != null)
|
|
{
|
|
network.Locked = false;
|
|
foreach (NewBond newbond in new_bonds)
|
|
{
|
|
newbond.master.Locked = false;
|
|
newbond.bond.Locked = false;
|
|
}
|
|
}
|
|
foreach (Host host in PIFs.Keys)
|
|
{
|
|
foreach (PIF pif in PIFs[host])
|
|
{
|
|
pif.Locked = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ReconfigureManagementInterfaces(List<PIF> slaves, PIF new_master, int hi)
|
|
{
|
|
int lo = PercentComplete;
|
|
int inc = (hi - lo) / slaves.Count;
|
|
foreach (PIF pif in slaves)
|
|
{
|
|
lo += inc;
|
|
|
|
// In Boston and later, the only thing we need to do is move the ManagementPurpose
|
|
// (= management interface name) to the bond (see PR-1006/CP-2059).
|
|
if (bostonOrGreater)
|
|
{
|
|
NetworkingActionHelpers.MoveManagementInterfaceName(this, pif, new_master);
|
|
}
|
|
else if (pif.management)
|
|
{
|
|
log.DebugFormat("Moving primary management interface to {0}...", pif.uuid);
|
|
NetworkingActionHelpers.ReconfigureSinglePrimaryManagement(this, pif, new_master, lo);
|
|
log.DebugFormat("Moving primary management interface to {0} done.", pif.uuid);
|
|
}
|
|
else if (pif.IsSecondaryManagementInterface(true))
|
|
{
|
|
log.DebugFormat("Moving secondary management interface to {0}...", pif.uuid);
|
|
ReconfigureSecondaryManagement(pif, new_master, lo);
|
|
log.DebugFormat("Moving secondary management interface to {0} done.", pif.uuid);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ReconfigureSecondaryManagement(PIF src, PIF dest, int hi)
|
|
{
|
|
System.Diagnostics.Trace.Assert(!bostonOrGreater);
|
|
|
|
int mid = (PercentComplete + hi) / 2;
|
|
|
|
PIF new_dest = NetworkingHelper.CopyIPConfig(src, dest);
|
|
|
|
NetworkingActionHelpers.BringDown(this, src, mid);
|
|
NetworkingActionHelpers.BringUp(this, new_dest, dest, hi);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Nothrow guarantee.
|
|
/// </summary>
|
|
/// <param name="slaves"></param>
|
|
/// <param name="master"></param>
|
|
private void RevertManagementInterfaces(NewBond new_bond)
|
|
{
|
|
List<PIF> slaves = new_bond.slaves;
|
|
PIF master = new_bond.master;
|
|
Bond bond = new_bond.bond;
|
|
|
|
PIF slave = Connection.Resolve(bond.primary_slave);
|
|
|
|
if (slave == null)
|
|
{
|
|
if (slaves.Count == 0)
|
|
return;
|
|
slave = slaves[0];
|
|
}
|
|
|
|
try
|
|
{
|
|
if (bostonOrGreater)
|
|
{
|
|
NetworkingActionHelpers.MoveManagementInterfaceName(this, master, slave);
|
|
}
|
|
else if (master.management)
|
|
{
|
|
log.DebugFormat("Reverting primary management interface change for {0} as part of cleanup...", master.uuid);
|
|
NetworkingActionHelpers.ReconfigurePrimaryManagement(this, master, slave, PercentComplete);
|
|
log.DebugFormat("Reverting primary management interface change for {0} as part of cleanup done.", master.uuid);
|
|
}
|
|
else if (master.IsSecondaryManagementInterface(true))
|
|
{
|
|
log.DebugFormat("Reverting secondary management interface change for {0} as part of cleanup...", master.uuid);
|
|
ReconfigureSecondaryManagement(master, slave, PercentComplete);
|
|
log.DebugFormat("Reverting secondary management interface change for {0} as part of cleanup done.", master.uuid);
|
|
}
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Warn(exn, exn);
|
|
}
|
|
}
|
|
|
|
string CreateNetwork(int lo, int hi)
|
|
{
|
|
log.DebugFormat("Creating network {0}...", name_label);
|
|
|
|
XenAPI.Network network = new XenAPI.Network();
|
|
network.name_label = name_label;
|
|
network.AutoPlug = autoplug;
|
|
if (Helpers.CowleyOrGreater(Connection))
|
|
network.MTU = mtu;
|
|
if (network.other_config == null)
|
|
network.other_config = new Dictionary<string, string>();
|
|
network.other_config[XenAPI.Network.CREATE_IN_PROGRESS] = "true";
|
|
|
|
RelatedTask = XenAPI.Network.async_create(Session, network);
|
|
PollToCompletion(lo, hi);
|
|
|
|
log.DebugFormat("Created network {0} as {1}.", name_label, Result);
|
|
|
|
return Result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Nothrow guarantee.
|
|
/// </summary>
|
|
/// <param name="network"></param>
|
|
private void DestroyNetwork(string network)
|
|
{
|
|
log.DebugFormat("Destroying network {0} as part of cleanup...", network);
|
|
|
|
try
|
|
{
|
|
XenAPI.Network.destroy(Session, network);
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Warn(exn, exn);
|
|
}
|
|
|
|
log.DebugFormat("Destroying network {0} as part of cleanup done.", network);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Nothrow guarantee.
|
|
/// </summary>
|
|
/// <param name="bond"></param>
|
|
private void DestroyBond(Bond bond)
|
|
{
|
|
log.DebugFormat("Destroying bond {0} as part of cleanup...", bond.uuid);
|
|
|
|
try
|
|
{
|
|
XenAPI.Bond.destroy(Session, bond.opaque_ref);
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Warn(exn, exn);
|
|
}
|
|
|
|
log.DebugFormat("Destroying bond {0} as part of cleanup done.", bond.uuid);
|
|
}
|
|
|
|
protected override void Clean()
|
|
{
|
|
Connection.ExpectDisruption = false;
|
|
UnlockAll(network, new_bonds);
|
|
}
|
|
}
|
|
}
|