/* 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.Text; using XenAdmin.Core; using XenAdmin.Network; using XenAPI; namespace XenAdmin.Actions { public class NetworkAction : AsyncAction { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private enum network_actions { create, destroy, update }; private XenAPI.Network networkClone; private XenAPI.Network networkServerObject; private readonly network_actions actionType; private readonly PIF pif; private readonly long vlan; private readonly bool external; private readonly bool changePIFs; private List PIFs; /// /// Create an external (VLAN) network. /// /// A new Network instance describing the changes to be made on the server side. /// The PIF representing the physical NIC from which we're basing our new VLAN. /// The new VLAN tag. public NetworkAction(IXenConnection connection, XenAPI.Network network, PIF pif, long vlan) : base(connection, string.Format(Messages.NETWORK_ACTION_CREATING_NETWORK_TITLE, network.Name(), Helpers.GetName(connection))) { actionType = network_actions.create; this.networkClone = network; this.pif = pif; this.vlan = vlan; this.external = true; this.PIFs = null; #region RBAC Dependencies ApiMethodsToRoleCheck.Add("network.create"); ApiMethodsToRoleCheck.Add("pool.create_VLAN_from_PIF"); #endregion init(); } /// /// Create or destroy a private network. /// public NetworkAction(IXenConnection connection, XenAPI.Network network, bool create) : base(connection, string.Format(create ? Messages.NETWORK_ACTION_CREATING_NETWORK_TITLE : Messages.NETWORK_ACTION_REMOVING_NETWORK_TITLE, network.Name(), Helpers.GetName(connection))) { this.networkClone = network; this.external = false; if (create) { #region RBAC Dependencies ApiMethodsToRoleCheck.Add("network.create"); #endregion actionType = network_actions.create; PIFs = null; } else { PIFs = Connection.ResolveAll(network.PIFs); #region RBAC Dependencies ApiMethodsToRoleCheck.Add("network.destroy"); if (PIFs.Find(p => p.IsTunnelAccessPIF()) != null) ApiMethodsToRoleCheck.Add("tunnel.destroy"); if (PIFs.Find(p => !p.IsTunnelAccessPIF() && p.physical) != null) ApiMethodsToRoleCheck.Add("pif.forget"); // actually, we should have at most one of tunnel.destroy and pif.forget if (PIFs.Find(p => !p.IsTunnelAccessPIF() && !p.physical) != null) ApiMethodsToRoleCheck.Add("vlan.destroy"); // same here, shouldn't be both virtual and physcial really #endregion actionType = network_actions.destroy; } init(); } /// /// Update a network. /// /// The modified network that we're going to save to the server. /// True if we're going to create or destroy PIFs (i.e. change a private network to a /// VLAN, or vice versa. /// Whether the new network is external i.e. a VLAN. /// The PIF representing the physical NIC from which we're basing our new VLAN. /// Null iff changePIFs is false or external is false. /// The new VLAN tag. Ignored iff changePIFs is false or external is false. /// public NetworkAction(IXenConnection connection, XenAPI.Network network, bool changePIFs, bool external, PIF pif, long vlan, bool suppressHistory) : base(connection, string.Format(Messages.NETWORK_ACTION_UPDATING_NETWORK_TITLE, network.Name(), Helpers.GetName(connection)), suppressHistory) { actionType = network_actions.update; this.networkClone = network; this.changePIFs = changePIFs; this.external = external; this.pif = pif; this.vlan = vlan; PIFs = changePIFs ? Connection.ResolveAll(network.PIFs) : null; #region RBAC Dependencies ApiMethodsToRoleCheck.Add("network.set_name_label"); ApiMethodsToRoleCheck.Add("network.set_other_config"); ApiMethodsToRoleCheck.Add("network.add_to_other_config"); ApiMethodsToRoleCheck.Add("network.remove_from_other_config"); ApiMethodsToRoleCheck.Add("network.set_tags"); ApiMethodsToRoleCheck.Add("network.add_tags"); ApiMethodsToRoleCheck.Add("network.remove_tags"); if (changePIFs) { ApiMethodsToRoleCheck.Add("pif.destroy"); if (external) ApiMethodsToRoleCheck.Add("pool.create_VLAN_from_PIF"); } #endregion init(); } private void init() { SetAppliesTo(Connection); if (networkClone.opaque_ref != null) { // we are updating an existing object, and so should lock it networkServerObject = Connection.Resolve(new XenRef(networkClone.opaque_ref)); networkServerObject.Locked = true; } if (PIFs == null) return; foreach (PIF pif in PIFs) pif.Locked = true; } private void SetAppliesTo(IXenConnection connection) { foreach (Pool pool in connection.Cache.Pools) { AppliesTo.Add(pool.opaque_ref); } foreach (Host host in connection.Cache.Hosts) { AppliesTo.Add(host.opaque_ref); } } private void destroyPIFs() { foreach (PIF pif in PIFs) { if (pif.IsTunnelAccessPIF()) { // A tunnel access PIF is destroyed by destroying its tunnel. // (Actually each network will have either all tunnel access PIFs (if // it is a CHIN) or all regular PIFs (if it isn't), but we don't use // that: we do it PIF-by-PIF). foreach (Tunnel tunnel in Connection.ResolveAll(pif.tunnel_access_PIF_of)) // actually there will only ever be one Tunnel.destroy(Session, tunnel.opaque_ref); } else { if (!pif.physical) { VLAN.destroy(Session, pif.VLAN_master_of); } else { // do we ever destroy physical pifs anyway? not sure this is something we need but here for completeness PIF.forget(Session, pif.opaque_ref); } } } PIFs = null; } protected override void Run() { switch (actionType) { case network_actions.destroy: Description = Messages.NETWORK_ACTION_REMOVING_NETWORK; destroyPIFs(); XenAPI.Network.destroy(Session, networkClone.opaque_ref); networkClone = null; Description = Messages.NETWORK_ACTION_NETWORK_REMOVED; break; case network_actions.update: Description = Messages.NETWORK_ACTION_UPDATING_NETWORK; if (changePIFs) { if (external) { //Before we do a destroy check the vlan tag is not in use on this network foreach (PIF p in Connection.Cache.PIFs) { if (p.VLAN == vlan && p.device == pif.device) throw new Exception(FriendlyErrorNames.PIF_VLAN_EXISTS); } } destroyPIFs(); if (external) CreateVLAN(networkClone.opaque_ref); } Description = Messages.NETWORK_ACTION_NETWORK_UPDATED; break; case network_actions.create: Description = Messages.NETWORK_ACTION_CREATING_NETWORK; XenRef networkRef = XenAPI.Network.create(Session, networkClone); if (external) CreateVLAN(networkRef.opaque_ref); Description = Messages.NETWORK_ACTION_NETWORK_CREATED; break; } } private void CreateVLAN(string network_ref) { Host host = Helpers.GetMaster(Connection); if (host == null) throw new Failure(Failure.INTERNAL_ERROR, "Can't find master"); Pool.create_VLAN_from_PIF(Session, pif.opaque_ref, network_ref, vlan); } protected override void Clean() { if (networkServerObject != null) networkServerObject.Locked = false; if (PIFs == null) return; foreach (PIF pif in PIFs) pif.Locked = false; } } }