# pylint: disable=C0302 """Calxeda: fabric.py """ # Copyright (c) 2012-2013, Calxeda 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. # * Neither the name of Calxeda Inc. nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # 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 HOLDERS 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. import time from cxmanage_api.tasks import DEFAULT_TASK_QUEUE from cxmanage_api.tftp import InternalTftp from cxmanage_api.node import Node as NODE from cxmanage_api.cx_exceptions import CommandFailedError, TimeoutError, \ IpmiError, TftpException # pylint: disable=R0902,R0903, R0904 class Fabric(object): """ The Fabric class provides management of multiple nodes. >>> from cxmanage_api.fabric import Fabric >>> fabric = Fabric('10.20.1.9') :param ip_address: The ip_address of ANY known node for the Fabric. :type ip_address: string :param username: The login username credential. [Default admin] :type username: string :param password: The login password credential. [Default admin] :type password: string :param tftp: Tftp server to facilitate IPMI command responses. :type tftp: `Tftp `_ :param task_queue: TaskQueue to use for sending commands. :type task_queue: `TaskQueue `_ :param verbose: Flag to turn on verbose output (cmd/response). :type verbose: boolean :param node: Node type, for dependency integration. :type node: `Node `_ """ class CompositeBMC(object): """ Composite BMC object. Provides a mechanism to run BMC commands in parallel across all nodes. """ def __init__(self, fabric): self.fabric = fabric def __getattr__(self, name): """ If the underlying BMCs have a method by this name, then return a callable function that does it in parallel across all nodes. """ nodes = self.fabric.nodes task_queue = self.fabric.task_queue for node in nodes.values(): if ((not hasattr(node.bmc, name)) or (not hasattr(getattr(node.bmc, name), "__call__"))): raise AttributeError( "'CompositeBMC' object has no attribute '%s'" % name ) def function(*args, **kwargs): """ Run the named BMC command in parallel across all nodes. """ tasks = {} for node_id, node in nodes.iteritems(): tasks[node_id] = task_queue.put( getattr(node.bmc, name), *args, **kwargs ) results = {} errors = {} for node_id, task in tasks.items(): task.join() if task.status == "Completed": results[node_id] = task.result else: errors[node_id] = task.error if errors: raise CommandFailedError(results, errors) return results return function # pylint: disable=R0913 def __init__(self, ip_address, username="admin", password="admin", tftp=None, ecme_tftp_port=5001, task_queue=None, verbose=False, node=None): """Default constructor for the Fabric class.""" self.ip_address = ip_address self.username = username self.password = password self._tftp = tftp self.ecme_tftp_port = ecme_tftp_port self.task_queue = task_queue self.verbose = verbose self.node = node self.cbmc = Fabric.CompositeBMC(self) self._nodes = {} if (not self.node): self.node = NODE if (not self.task_queue): self.task_queue = DEFAULT_TASK_QUEUE def __eq__(self, other): """__eq__() override.""" return (isinstance(other, Fabric) and self.nodes == other.nodes) def __hash__(self): """__hash__() override.""" return hash(tuple(self.nodes.iteritems())) def __str__(self): """__str__() override.""" return 'Fabric Node 0: %s (%d nodes)' % (self.nodes[0].ip_address, len(self.nodes)) @property def tftp(self): """Returns the tftp server for this Fabric. >>> fabric.tftp :return: The tftp server. :rtype: `Tftp `_ """ if (not self._tftp): self._tftp = InternalTftp.default() return self._tftp @tftp.setter def tftp(self, value): """ Set the TFTP server for this fabric (and all nodes) """ self._tftp = value if not self._nodes: return for node in self.nodes.values(): node.tftp = value @property def nodes(self): """List of nodes in this fabric. >>> fabric.nodes { 0: , 1: , 2: , 3: } .. note:: * Fabric nodes are lazily initialized. :returns: A mapping of node ids to node objects. :rtype: dictionary """ if not self._nodes: self.refresh() return self._nodes @property def primary_node(self): """The node to use for fabric config operations. Today, this is always node 0. >>> fabric.primary_node :return: Node object for primary node :rtype: Node object """ return self.nodes[0] def refresh(self, wait=False, timeout=600): """Gets the nodes of this fabric by pulling IP info from a BMC.""" def get_nodes(): """Returns a dictionary of nodes reported by the primary node IP""" new_nodes = {} node = self.node( ip_address=self.ip_address, username=self.username, password=self.password, tftp=self.tftp, ecme_tftp_port=self.ecme_tftp_port, verbose=self.verbose ) ipinfo = node.get_fabric_ipinfo() for node_id, node_address in ipinfo.iteritems(): new_nodes[node_id] = self.node( ip_address=node_address, username=self.username, password=self.password, tftp=self.tftp, ecme_tftp_port=self.ecme_tftp_port, verbose=self.verbose ) new_nodes[node_id].node_id = node_id return new_nodes initial_node_count = len(self._nodes) self._nodes = {} if wait: deadline = time.time() + timeout while time.time() < deadline: try: self._nodes = get_nodes() if len(self._nodes) >= initial_node_count: break except (IpmiError, TftpException): pass else: raise TimeoutError( "Fabric refresh timed out. Rediscovered %i of %i nodes" % (len(self._nodes), initial_node_count) ) else: self._nodes = get_nodes() def get_mac_addresses(self): """Gets MAC addresses from all nodes. >>> fabric.get_mac_addresses() { 0: ['fc:2f:40:3b:ec:40', 'fc:2f:40:3b:ec:41', 'fc:2f:40:3b:ec:42'], 1: ['fc:2f:40:91:dc:40', 'fc:2f:40:91:dc:41', 'fc:2f:40:91:dc:42'], 2: ['fc:2f:40:ab:f7:14', 'fc:2f:40:ab:f7:15', 'fc:2f:40:ab:f7:16'], 3: ['fc:2f:40:88:b3:6c', 'fc:2f:40:88:b3:6d', 'fc:2f:40:88:b3:6e'] } :return: The MAC addresses for each node. :rtype: dictionary """ return self.primary_node.get_fabric_macaddrs() def get_uplink_info(self, async=False): """Gets the fabric uplink info. >>> fabric.get_uplink_info() {0: {'eth0': 0, 'eth1': 0, 'mgmt': 0} 1: {'eth0': 0, 'eth1': 0, 'mgmt': 0} 2: {'eth0': 0, 'eth1': 0, 'mgmt': 0} 3: {'eth0': 0, 'eth1': 0, 'mgmt': 0}} :param async: Flag that determines if the command result (dictionary) is returned or a Task object (can get status, etc.). :type async: boolean :return: The uplink info for each node. :rtype: dictionary """ return self._run_on_all_nodes(async, "get_uplink_info") def get_uplink_speed(self, async=False): """Gets the uplink speed of every node in the fabric. >>> fabric.get_uplink_speed() {0: 1, 1: 0, 2: 0, 3: 0} :param async: Flag that determines if the command result (dictionary) is returned or a Task object (can get status, etc.). :type async: boolean :return: The uplink info for each node. :rtype: dictionary """ return self._run_on_all_nodes(async, "get_uplink_speed") def get_power(self, async=False): """Returns the power status for all nodes. >>> fabric.get_power() {0: False, 1: False, 2: False, 3: False} :param async: Flag that determines if the command result (dictionary) is returned or a Command object (for cmd status, etc.). :type async: boolean :return: The power status of each node. :rtype: dictionary or `Task `__ """ return self._run_on_all_nodes(async, "get_power") def set_power(self, mode, async=False, ignore_existing_state=False): """Send an IPMI power command to all nodes. >>> # On ... >>> fabric.set_power(mode='on') >>> # Off ... >>> fabric.set_power(mode='off') >>> # Sanity check ... >>> fabric.get_power() {0: False, 1: False, 2: False, 3: False} :param mode: Mode to set the power to (for all nodes). :type mode: string :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean :param ignore_existing_state: Flag that allows the caller to only try to turn on or off nodes that are not turned on or off, respectively. :type ignore_existing_state: boolean """ self._run_on_all_nodes(async, "set_power", mode, ignore_existing_state) def get_power_policy(self, async=False): """Gets the power policy from all nodes. >>> fabric.get_power_policy() {0: 'always-on', 1: 'always-on', 2: 'always-on', 3: 'always-on'} :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean :return: The power policy for all nodes on this fabric. :rtype: dictionary or `Task `__ """ return self._run_on_all_nodes(async, "get_power_policy") def set_power_policy(self, state, async=False): """Sets the power policy on all nodes. >>> fabric.set_power_policy(state='always-off') >>> # Check to see if it took ... >>> fabric.get_power_policy() {0: 'always-off', 1: 'always-off', 2: 'always-off', 3: 'always-off'} :param state: State to set the power policy to for all nodes. :type state: string :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean """ self._run_on_all_nodes(async, "set_power_policy", state) def mc_reset(self, wait=False, async=False): """Resets the management controller on all nodes. >>> fabric.mc_reset() :param wait: Wait for the nodes to come back up. :type wait: boolean :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean """ self._run_on_all_nodes(async, "mc_reset", wait) def get_sensors(self, search="", async=False): """Gets sensors from all nodes. >>> fabric.get_sensors() { 0: { 'DRAM VDD Current' : , 'DRAM VDD Voltage' : , 'MP Temp 0' : , 'Node Power' : , 'TOP Temp 0' : , 'TOP Temp 1' : , 'TOP Temp 2' : , 'Temp 0' : , 'Temp 1' : , 'Temp 2' : , 'Temp 3' : , 'V09 Current' : , 'V09 Voltage' : , 'V18 Current' : , 'V18 Voltage' : , 'VCORE Current' : , 'VCORE Power' : , 'VCORE Voltage' : }, # # Output trimmed for brevity ... The output would be the same # (format) for the remaining 3 ECMEs on this system. # }, .. note:: * Output condensed for brevity. * If the name parameter is not specified, all sensors are returned. :param name: Name of the sensor to get. (for all nodes) :type name: string :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean """ return self._run_on_all_nodes(async, "get_sensors", search) def get_firmware_info(self, async=False): """Gets the firmware info from all nodes. >>> fabric.get_firmware_info() { 0: [, ...], 1: [, ...], 2: [, ...], 3: [, ...] } :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean :return: THe firmware info for all nodes. :rtype: dictionary or `Task `__ """ return self._run_on_all_nodes(async, "get_firmware_info") def get_firmware_info_dict(self, async=False): """Gets the firmware info from all nodes. >>> fabric.get_firmware_info_dict() {0: [ # # Each dictionary (in order) in this list represents the # corresponding partition information # {# Partition 0 'daddr' : '20029000', 'flags' : 'fffffffd', 'in_use' : 'Unknown', 'offset' : '00000000', 'partition' : '00', 'priority' : '0000000c', 'size' : '00005000', 'type' : '02 (S2_ELF)', 'version' : 'v0.9.1' }, # Partitions 1 - 17 ], # # Output trimmed for brevity ... The remaining Nodes in the Fabric # would display all the partition format in the same manner. # } :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean :return: The firmware info for all nodes. :rtype: dictionary or `Task `__ """ return self._run_on_all_nodes(async, "get_firmware_info_dict") def is_updatable(self, package, partition_arg="INACTIVE", priority=None, async=False): """Checks to see if all nodes can be updated with this fw package. >>> fabric.is_updatable(package=fwpkg) {0: True, 1: True, 2: True, 3: True} :param package: Firmware package to test for updating. :type package: `FirmwarePackage `_ :param partition: Partition to test for updating. :type partition: string :param priority: SIMG Header priority. :type priority: integer :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean :return: Whether or not a node can be updated with the specified firmware package. :rtype: dictionary or `Task `__ """ return self._run_on_all_nodes(async, "is_updatable", package, partition_arg, priority) def update_firmware(self, package, partition_arg="INACTIVE", priority=None, async=False): """Updates the firmware on all nodes. >>> fabric.update_firmware(package=fwpkg) :param package: Firmware package to update to. :type package: `FirmwarePackage `_ :param partition_arg: Which partition to update. :type partition_arg: string :param priority: SIMG header Priority setting. :type priority: integer :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean """ self._run_on_all_nodes(async, "update_firmware", package, partition_arg, priority) def config_reset(self, async=False): """Resets the configuration on all nodes to factory defaults. >>> fabric.config_reset() {0: None, 1: None, 2: None, 3: None} :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean """ self._run_on_all_nodes(async, "config_reset") def set_boot_order(self, boot_args, async=False): """Sets the boot order on all nodes. >>> fabric.set_boot_order(boot_args=['pxe', 'disk']) :param boot_args: Boot order list. :type boot_args: list :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean """ self._run_on_all_nodes(async, "set_boot_order", boot_args) def get_boot_order(self, async=False): """Gets the boot order from all nodes. >>> fabric.get_boot_order() { 0: ['disk', 'pxe'], 1: ['disk', 'pxe'], 2: ['disk', 'pxe'], 3: ['disk', 'pxe'] } :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean :returns: The boot order of each node on this fabric. :rtype: dictionary or `Task `__ """ return self._run_on_all_nodes(async, "get_boot_order") def set_pxe_interface(self, interface, async=False): """Sets the pxe interface on all nodes. >>> fabric.set_pxe_interface(interface='eth0') :param interface: Inteface for pxe requests :type interface: string :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean """ self._run_on_all_nodes(async, "set_pxe_interface", interface) def get_pxe_interface(self, async=False): """Gets the pxe interface from all nodes. >>> fabric.get_pxe_interface() { 0: 'eth0', 1: 'eth0', 2: 'eth0', 3: 'eth0' } :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean :returns: The boot order of each node on this fabric. :rtype: dictionary or `Task `__ """ return self._run_on_all_nodes(async, "get_pxe_interface") def get_versions(self, async=False): """Gets the version info from all nodes. >>> fabric.get_versions() { 0: , 1: , 2: , 3: } .. seealso:: `Node.get_versions() \ `_ :param async: Flag that determines if the command result (dictionary) is returned or a Command object (can get status, etc.). :type async: boolean :returns: The basic SoC info for all nodes. :rtype: dictionary or `Task `__ """ return self._run_on_all_nodes(async, "get_versions") def get_versions_dict(self, async=False): """Gets the version info from all nodes. >>> fabric.get_versions_dict() {0: { 'a9boot_version' : 'v2012.10.16', 'bootlog_version' : 'v0.9.1-39-g7e10987', 'build_number' : '7E10987C', 'card' : 'EnergyCard X02', 'cdb_version' : 'v0.9.1-39-g7e10987', 'dtb_version' : 'v3.6-rc1_cx_2012.10.02', 'header' : 'Calxeda SoC (0x0096CD)', 'soc_version' : 'v0.9.1', 'stage2_version' : 'v0.9.1', 'timestamp' : '1352911670', 'uboot_version' : 'v2012.07_cx_2012.10.29', 'ubootenv_version' : 'v2012.07_cx_2012.10.29', 'version' : 'ECX-1000-v1.7.1' }, # # Output trimmed for brevity ... Each remaining Nodes get_versions # dictionary would be printed. # } .. seealso:: `Node.get_versions_dict() \ `_ :param async: Flag that determines if the command result (dictionary) is returned or a Task object (can get status, etc.). :type async: boolean :returns: The basic SoC info for all nodes. :rtype: dictionary or `Task `__ """ return self._run_on_all_nodes(async, "get_versions_dict") def ipmitool_command(self, ipmitool_args, asynchronous=False): """Run an arbitrary IPMItool command on all nodes. >>> # Gets eth0's MAC Address for each node ... >>> fabric.ipmitool_command(['cxoem', 'fabric', 'get', 'macaddr', >>> ...'interface', '0']) { 0: 'fc:2f:40:3b:ec:40', 1: 'fc:2f:40:91:dc:40', 2: 'fc:2f:40:ab:f7:14', 3: 'fc:2f:40:88:b3:6c' } :param ipmitool_args: Arguments to pass on to the ipmitool command. :type ipmitool_args: list :param async: Flag that determines if the command result (dictionary) is returned or a Task object (can get status, etc.). :type async: boolean :returns: IPMI command response. :rtype: string """ return self._run_on_all_nodes(asynchronous, "ipmitool_command", ipmitool_args) def get_ubootenv(self, async=False): """Gets the u-boot environment from all nodes. >>> fabric.get_ubootenv() { 0: , 1: , 2: , 3: } :param async: Flag that determines if the command result (dictionary) is returned or a Task object (can get status, etc.). :type async: boolean :returns: UBootEnvironment objects for all nodes. :rtype: dictionary or `Task `_ """ return self._run_on_all_nodes(async, "get_ubootenv") # pylint: disable=R0913 def get_server_ip(self, interface=None, ipv6=False, user="user1", password="1Password", aggressive=False, async=False): """Get the server IP address from all nodes. The nodes must be powered on for this to work. >>> fabric.get_server_ip() { 0: '192.168.100.100', 1: '192.168.100.101', 2: '192.168.100.102', 3: '192.168.100.103' } :param interface: Network interface to check (e.g. eth0). :type interface: string :param ipv6: Return an IPv6 address instead of IPv4. :type ipv6: boolean :param user: Linux username. :type user: string :param password: Linux password. :type password: string :param aggressive: Discover the IP aggressively (may power cycle node). :type aggressive: boolean :param async: Flag that determines if the command result (dictionary) is returned or a Task object (can get status, etc.). :type async: boolean :return: Server IP addresses for all nodes.. :rtype: dictionary or `Task `_ """ return self._run_on_all_nodes(async, "get_server_ip", interface, ipv6, user, password, aggressive) def get_ipsrc(self): """Return the ipsrc for the fabric. >>> fabric.get_ipsrc() 2 :return: 1 for static, 2 for DHCP :rtype: integer """ return self.primary_node.bmc.fabric_config_get_ip_src() def set_ipsrc(self, ipsrc_mode): """Set the ipsrc for the fabric. >>> fabric.set_ipsrc(2) :param ipsrc_mode: 1 for static, 2 for DHCP :type ipsrc_mode: integer """ self.primary_node.bmc.fabric_config_set_ip_src(ipsrc_mode) def apply_factory_default_config(self): """Sets the fabric config to factory default >>> fabric.apply_factory_default_config() """ self.primary_node.bmc.fabric_config_factory_default() def get_ipaddr_base(self): """The base IPv4 address for a range of static IP addresses used for the nodes in the fabric >>> fabric.get_ipaddr_base() '192.168.100.1' :return: The first IP address in the range of static IP addresses :rtype: string """ return self.primary_node.bmc.fabric_config_get_ip_addr_base() def update_config(self): """Push out updated configuration data for all nodes in the fabric. >>> fabric.update_config() """ self.primary_node.bmc.fabric_config_update_config() def get_linkspeed(self): """Get the global linkspeed for the fabric. In the partition world this means the linkspeed for Configuration 0, Partition 0, Profile 0. >>> fabric.get_linkspeed() 2.5 :return: Linkspeed for the fabric. :rtype: float """ return self.primary_node.bmc.fabric_config_get_linkspeed() def set_linkspeed(self, linkspeed): """Set the global linkspeed for the fabric. In the partition world this means the linkspeed for Configuration 0, Partition 0, Profile 0. >>> fabric.set_linkspeed(10) :param linkspeed: Linkspeed specified in Gbps. :type linkspeed: float """ self.primary_node.bmc.fabric_config_set_linkspeed(linkspeed) def add_macaddr(self, nodeid, iface, macaddr): """Add a new macaddr to a node/interface in the fabric. >>> fabric.add_macaddr(3, 1, "66:55:44:33:22:11") :param nodeid: Node id to which the macaddr is to be added :type nodeid: integer :param iface: interface on the node to which the macaddr is to be added :type iface: integer :param macaddr: mac address to be added :type macaddr: string """ self.primary_node.bmc.fabric_add_macaddr(nodeid=nodeid, iface=iface, macaddr=macaddr) def rm_macaddr(self, nodeid, iface, macaddr): """Remove a macaddr to a node/interface in the fabric. >>> fabric.rm_macaddr(3, 1, "66:55:44:33:22:11") :param nodeid: Node id from which the macaddr is to be remove :type nodeid: integer :param iface: interface on the node which the macaddr is to be removed :type iface: integer :param macaddr: mac address to be removed :type macaddr: string """ self.primary_node.bmc.fabric_rm_macaddr(nodeid=nodeid, iface=iface, macaddr=macaddr) def set_macaddr_base(self, macaddr): """ Set a base MAC address for a custom range. >>> fabric.set_macaddr_base("66:55:44:33:22:11") :param macaddr: mac address base to use :type macaddr: string """ self.primary_node.bmc.fabric_config_set_macaddr_base(macaddr=macaddr) def get_macaddr_base(self): """ Get the base MAC address for custom ranges. >>> fabric.get_macaddr_base() '08:00:00:00:08:5c' :return: mac address base :rtype: string """ return self.primary_node.bmc.fabric_config_get_macaddr_base() def set_macaddr_mask(self, mask): """ Set MAC address mask for a custom range. >>> fabric.set_macaddr_mask("ff:ff:ff:ff:ff:00") :param macaddr: mac address mask to use :type macaddr: string """ self.primary_node.bmc.fabric_config_set_macaddr_mask(mask=mask) def get_macaddr_mask(self): """ Get the MAC address mask for custom ranges. >>> fabric.get_macaddr_mask() '08:00:00:00:08:5c' :return: mac address mask :rtype: string """ return self.primary_node.bmc.fabric_config_get_macaddr_mask() def get_linkspeed_policy(self): """Get the global linkspeed policy for the fabric. In the partition world this means the linkspeed for Configuration 0, Partition 0, Profile 0. >>> fabric.get_linkspeed_policy() 1 :return: Linkspeed Policy for the fabric. :rtype: integer """ return self.primary_node.bmc.fabric_config_get_linkspeed_policy() def set_linkspeed_policy(self, ls_policy): """Set the global linkspeed policy for the fabric. In the partition world this means the linkspeed policy for Configuration 0, Partition 0, Profile 0. >>> fabric.set_linkspeed_policy(1) :param linkspeed: Linkspeed Policy. 0: Fixed, 1: Topological :type linkspeed: integer """ self.primary_node.bmc.fabric_config_set_linkspeed_policy(ls_policy) def get_link_users_factor(self): """Get the global link users factor for the fabric. In the partition world this means the link users factor for Configuration 0, Partition 0, Profile 0. >>> fabric.get_link_users_factor() 1 :return: Link users factor for the fabric. :rtype: integer """ return self.primary_node.bmc.fabric_config_get_link_users_factor() def set_link_users_factor(self, lu_factor): """Set the global link users factor for the fabric. In the partition world this means the link users factor for Configuration 0, Partition 0, Profile 0. >>> fabric.set_link_users_factor(10) :param lu_factor: Multiplying factor for topological linkspeeds :type lu_factor: integer """ self.primary_node.bmc.fabric_config_set_link_users_factor(lu_factor) def get_uplink(self, iface=0): """Get the uplink for an interface to xmit a packet out of the cluster. >>> fabric.get_uplink(0) 0 :param iface: The interface for the uplink. :type iface: integer :return: The uplink iface is using. :rtype: integer """ return self.primary_node.bmc.fabric_config_get_uplink(iface=iface) def set_uplink(self, uplink=0, iface=0): """Set the uplink for an interface to xmit a packet out of the cluster. >>> fabric.set_uplink(uplink=0,iface=0) :param uplink: The uplink to set. :type uplink: integer :param iface: The interface for the uplink. :type iface: integer """ self.primary_node.bmc.fabric_config_set_uplink(uplink=uplink, iface=iface) def get_link_stats(self, link=0, async=False): """Get the link_stats for each node in the fabric. >>> fabric.get_link_stats() {0: {'FS_LC0_BYTE_CNT_0': '0x0', 'FS_LC0_BYTE_CNT_1': '0x0', 'FS_LC0_CFG_0': '0x1000d07f', 'FS_LC0_CFG_1': '0x105f', 'FS_LC0_CM_RXDATA_0': '0x0', 'FS_LC0_CM_RXDATA_1': '0x0', 'FS_LC0_CM_TXDATA_0': '0x82000002', 'FS_LC0_CM_TXDATA_1': '0x0', 'FS_LC0_PKT_CNT_0': '0x0', 'FS_LC0_PKT_CNT_1': '0x0', 'FS_LC0_RDRPSCNT': '0x3e89f', 'FS_LC0_RERRSCNT': '0x0', 'FS_LC0_RMCSCNT': '0x174b9bf', 'FS_LC0_RPKTSCNT': '0x0', 'FS_LC0_RUCSCNT': '0x43e9b', 'FS_LC0_SC_STAT': '0x0', 'FS_LC0_STATE': '0x1033', 'FS_LC0_TDRPSCNT': '0x0', 'FS_LC0_TPKTSCNT': '0x1'}, }} >>> # >>> # Output trimmed for brevity ... >>> # The data shown for node 0 is the same type of data for each >>> # node in the fabric. >>> # :param link: The link to get stats for (0-4). :type link: integer :param async: Flag that determines if the command result (dictionary) is returned or a Task object (can get status, etc.). :type async: boolean :returns: The link_stats for each link on each node. :rtype: dictionary """ return self._run_on_all_nodes(async, "get_link_stats", link) def get_linkmap(self, async=False): """Get the linkmap for each node in the fabric. >>> fabric.get_linkmap() {0: {1: 2, 3: 1, 4: 3}, 1: {3: 0}, 2: {3: 0, 4: 3}, 3: {3: 0, 4: 2}} :param async: Flag that determines if the command result (dictionary) is returned or a Task object (can get status, etc.). :type async: boolean :returns: The linkmap for each node. :rtype: dectionary """ return self._run_on_all_nodes(async, "get_linkmap") def get_routing_table(self, async=False): """Get the routing_table for the fabric. >>> fabric.get_routing_table() {0: {1: [0, 0, 0, 3, 0], 2: [0, 3, 0, 0, 2], 3: [0, 2, 0, 0, 3]}, 1: {0: [0, 0, 0, 3, 0], 2: [0, 0, 0, 2, 0], 3: [0, 0, 0, 2, 0]}, 2: {0: [0, 0, 0, 3, 2], 1: [0, 0, 0, 2, 0], 3: [0, 0, 0, 2, 3]}, 3: {0: [0, 0, 0, 3, 2], 1: [0, 0, 0, 2, 0], 2: [0, 0, 0, 2, 3]}} :param async: Flag that determines if the command result (dictionary) is returned or a Task object (can get status, etc.). :type async: boolean :returns: The routing_table for the fabric. :rtype: dictionary """ return self._run_on_all_nodes(async, "get_routing_table") def get_depth_chart(self, async=False): """Get the depth_chart for the fabric. >>> fabric.get_depth_chart() {0: {1: {'shortest': (0, 0)}, 2: {'others': [(3, 1)], 'shortest': (0, 0)}, 3: {'others': [(2, 1)], 'shortest': (0, 0)}}, 1: {0: {'shortest': (1, 0)}, 2: {'others': [(3, 2)], 'shortest': (0, 1)}, 3: {'others': [(2, 2)], 'shortest': (0, 1)}}, 2: {0: {'others': [(3, 1)], 'shortest': (2, 0)}, 1: {'shortest': (0, 1)}, 3: {'others': [(0, 1)], 'shortest': (2, 0)}}, 3: {0: {'others': [(2, 1)], 'shortest': (3, 0)}, 1: {'shortest': (0, 1)}, 2: {'others': [(0, 1)], 'shortest': (3, 0)}}} :param async: Flag that determines if the command result (dictionary) is returned or a Task object (can get status, etc.). :type async: boolean :returns: The depth_chart for the fabric. :rtype: dictionary """ return self._run_on_all_nodes(async, "get_depth_chart") def _run_on_all_nodes(self, async, name, *args, **kwargs): """Start a command on all nodes.""" tasks = {} for node_id, node in self.nodes.iteritems(): tasks[node_id] = self.task_queue.put(getattr(node, name), *args, **kwargs) if async: return tasks else: results = {} errors = {} for node_id, task in tasks.iteritems(): task.join() if task.status == "Completed": results[node_id] = task.result else: errors[node_id] = task.error if errors: raise CommandFailedError(results, errors) return results # End of file: ./fabric.py