/* Copyright (c) Cloud Software Group, Inc. * * 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 XenAdmin; namespace XenAPI { public partial class PIF : IComparable { public override string Name() { if (IsPhysical()) { bool bond; string number = NICIdentifier(out bond); return string.Format(bond ? Messages.PIF_BOND : Messages.PIF_NIC, number); } else if (IsTunnelAccessPIF()) { // In the case of tunnel access PIFs, use the name of the corresponding transport PIF (CA-63296) Tunnel tunnel = Connection.Resolve(tunnel_access_PIF_of[0]); PIF transport_pif = Connection.Resolve(tunnel.transport_PIF); return transport_pif.Name(); } else if(IsSriovLogicalPIF()) { if (Connection == null) return ""; Network_sriov network_s = Connection.Resolve(sriov_logical_PIF_of[0]); if (network_s == null) return ""; PIF pif = Connection.Resolve(network_s.physical_PIF); if (pif == null) return ""; return pif.Name(); } else { if (Connection == null) return ""; VLAN vlan = Connection.Resolve(VLAN_master_of); if (vlan == null) return ""; PIF member = Connection.Resolve(vlan.tagged_PIF); if (member == null) return ""; return member.Name(); } } public PIF_metrics PIFMetrics() { return metrics == null || Connection == null ? null : Connection.Resolve(metrics); } /// /// This is the name of the secondary management interface /// public string GetManagementPurpose() { return Get(other_config, "management_purpose"); } public void SetManagementPurpose(string value) { other_config = SetDictionaryKey(other_config, "management_purpose", value); } internal string NICIdentifier(out bool is_bond) { if (bond_master_of.Count == 0) { is_bond = false; return device.Replace("eth", ""); } else { is_bond = true; if (Connection == null) return device; List members = new List(); foreach (Bond bond in Connection.ResolveAll(bond_master_of)) { members.AddRange(Connection.ResolveAll(bond.slaves)); } return BondSuffix(members); } } public static string BondSuffix(List members) { List ids = new List(); foreach (PIF member in members) { ids.Add(member.device.Replace("eth", "")); } ids.Sort(); return string.Join("+", ids.ToArray()); } public override string ToString() { return Name(); } public bool IsTunnelAccessPIF() { return tunnel_access_PIF_of != null && tunnel_access_PIF_of.Count != 0; } public bool IsPhysical() { return VLAN == -1 && !IsTunnelAccessPIF() && !IsSriovLogicalPIF(); } public override int CompareTo(PIF other) { int result = this.management.CompareTo(other.management); if (result != 0) return -result; // management PIF first return base.CompareTo(other); } public override bool Show(bool showHiddenVMs) { return managed && (showHiddenVMs || !IsHidden()); } public override bool IsHidden() { return BoolKey(other_config, HIDE_FROM_XENCENTER); } // Whether this PIF is a management interface in the XenCenter sense. // Note that this is not the same as PIF.management, which only includes // the primary management interface. We also want all interfaces with an // IP address. public bool IsManagementInterface(bool showHiddenVMs) { return IsPrimaryManagementInterface() || IsSecondaryManagementInterface(showHiddenVMs); } public bool IsPrimaryManagementInterface() { if (!management) return false; Network nw = Connection.Resolve(network); return nw != null && !nw.IsGuestInstallerNetwork(); } // I lied slightly above. A secondary management interface in Boston and greater // is one with an IP address. In Cowley and earlier, it's one where disallow_unplug // is set. If the interface was configured through XenCenter, both of these things // will be true, but if it was configured on the command line, they may not be. // See CA-56611 for a discussion of this. public bool IsSecondaryManagementInterface(bool showHiddenVMs) { if (management) return false; bool criterion = (ip_configuration_mode != ip_configuration_mode.None && ip_configuration_mode != ip_configuration_mode.unknown); if (!criterion) return false; Network nw = Connection.Resolve(network); return nw != null && nw.Show(showHiddenVMs); } public string ManagementInterfaceNameOrUnknown() { if (management) return Messages.MANAGEMENT; var managementPurpose = GetManagementPurpose(); return string.IsNullOrEmpty(managementPurpose) ? Messages.NETWORKING_PROPERTIES_PURPOSE_UNKNOWN : managementPurpose; } public bool IsBondMember() { return BondMemberOf() != null; } /// /// Whether this is a bond member, and the bond interface is plugged. /// public bool IsInUseBondMember() { Bond bond = BondMemberOf(); if (bond == null) return false; PIF bondInterface = bond.Connection.Resolve(bond.master); if (bondInterface == null) return false; return bondInterface.currently_attached; } public bool IsUsedByClustering() { return Connection.Cache.Cluster_hosts.Any(cluster => cluster.PIF.opaque_ref == opaque_ref); } /// /// Returns the Bond of which this PIF is a member, or null if it is not so. /// public Bond BondMemberOf() { return Connection == null ? null : Connection.Resolve(bond_slave_of); } /// /// Returns the Bond of which this PIF is an interface, or null if it is not so. /// public Bond BondInterfaceOf() { return Connection == null || bond_master_of.Count == 0 ? null : Connection.Resolve(bond_master_of[0]); } public string Speed() { if (Connection == null) return Messages.HYPHEN; PIF_metrics pifMetrics = Connection.Resolve(this.metrics); if (pifMetrics == null) return Messages.HYPHEN; return string.Format(Messages.NICPANEL_BIT_RATE, pifMetrics.speed); } public bool IsBondNIC() { return bond_master_of.Count > 0; } public string Duplex() { if (Connection == null) return Messages.HYPHEN; PIF_metrics pifMetrics = Connection.Resolve(this.metrics); if (pifMetrics == null) return Messages.HYPHEN; return pifMetrics.duplex ? Messages.NICPANEL_FULL_DUPLEX : Messages.NICPANEL_HALF_DUPLEX; } public bool Carrier() { if (Connection == null) return false; PIF_metrics pifMetrics = Connection.Resolve(this.metrics); if (pifMetrics == null) return false; return pifMetrics.carrier; } /// /// Returns either the IP address of the PIF, DHCP or Unknown as appropriate /// public string FriendlyIPAddress() { if (!string.IsNullOrEmpty(IP)) return IP; switch (ip_configuration_mode) { case ip_configuration_mode.DHCP: return Messages.PIF_DHCP; default: return Messages.PIF_UNKNOWN; } } public string LinkStatusString() { var linkStatus = LinkStatus(); switch (linkStatus) { case LinkState.Connected: return Messages.CONNECTED; case LinkState.Disconnected: return Messages.DISCONNECTED; case LinkState.Unknown: return Messages.UNKNOWN; } return "-"; } public enum LinkState { Unknown, Connected, Disconnected }; public LinkState LinkStatus() { if (IsTunnelAccessPIF()) { Tunnel tunnel = Connection.Resolve(tunnel_access_PIF_of[0]); // can only ever be the access PIF of one tunnel Dictionary status = (tunnel == null ? null : tunnel.status); return (status != null && status.ContainsKey("active") && status["active"] == "true" ? LinkState.Connected : LinkState.Disconnected); } //if (!pif.IsPhysical && !poolwide) // return Messages.SPACED_HYPHEN; PIF_metrics pifMetrics = PIFMetrics(); // LinkStatus of SR-IOV network and VLAN on SR-IOV network if (IsSriovLogicalPIF() || VLAN >= 0) { var network_sriov = NetworkSriov(); if (network_sriov == null && VLAN < 0) { return LinkState.Disconnected; } else if (network_sriov != null) { Network_sriov network_s = Connection.Resolve(network_sriov); if (pifMetrics == null) return LinkState.Unknown; else if (!pifMetrics.carrier || (network_s == null || network_s.configuration_mode == sriov_configuration_mode.unknown || network_s.requires_reboot == true )) return LinkState.Disconnected; } } return pifMetrics == null ? LinkState.Unknown : pifMetrics.carrier ? LinkState.Connected : LinkState.Disconnected; } public string IpConfigurationModeString() { switch (ip_configuration_mode) { case ip_configuration_mode.None: return Messages.PIF_NONE; case ip_configuration_mode.DHCP: return Messages.PIF_DHCP; case ip_configuration_mode.Static: return Messages.PIF_STATIC; default: return Messages.PIF_UNKNOWN; } } public bool FCoECapable() { return capabilities.Any(capability => capability == "fcoe"); } public bool IsSriovLogicalPIF() { return sriov_logical_PIF_of != null && sriov_logical_PIF_of.Count != 0; } public bool IsSriovPhysicalPIF() { return sriov_physical_PIF_of != null && sriov_physical_PIF_of.Count != 0; } public bool SriovCapable() { return capabilities.Any(capability => capability == "sriov"); } public XenRef NetworkSriov() { if (IsSriovLogicalPIF()) return sriov_logical_PIF_of[0]; if (VLAN < 0) return null; var vlan = Connection.Resolve(VLAN_master_of); if (vlan == null) return null; var taggedPif = Connection.Resolve(vlan.tagged_PIF); return taggedPif != null && taggedPif.IsSriovLogicalPIF() ? taggedPif.sriov_logical_PIF_of[0] : null; } } }