diff options
author | Sheldon Sandbekkhaug <sheldon.sandbekkhaug@calxeda.com> | 2013-08-09 15:20:06 -0500 |
---|---|---|
committer | Sheldon Sandbekkhaug <sheldon.sandbekkhaug@calxeda.com> | 2013-08-09 15:20:06 -0500 |
commit | ff798ac2b2952d8340fcc40158a103dbc38a740f (patch) | |
tree | a6a853be145ec34f16a414b5d55ef344d728ae79 | |
parent | 7adb81f7cc7b443c011a764f60ec0c8913d476b7 (diff) | |
download | cxmanage-ff798ac2b2952d8340fcc40158a103dbc38a740f.tar.gz |
Create Commands to Get FRU Versions
node.py
Added private method to read FRUs
Added get_node_fru_version()
Added get_slot_fru_version()
Removed unneeded import
Fabric.py
Added methods to get node FRU version and slot FRU version
Added a fru_version command for cxmanage: fru_version.py
Minor formatting in scripts/cxmanage
-rw-r--r-- | cxmanage/commands/fru_version.py | 69 | ||||
-rw-r--r-- | cxmanage_api/fabric.py | 38 | ||||
-rw-r--r-- | cxmanage_api/node.py | 64 | ||||
-rwxr-xr-x | scripts/cxmanage | 17 |
4 files changed, 185 insertions, 3 deletions
diff --git a/cxmanage/commands/fru_version.py b/cxmanage/commands/fru_version.py new file mode 100644 index 0000000..02ea0eb --- /dev/null +++ b/cxmanage/commands/fru_version.py @@ -0,0 +1,69 @@ + +# Copyright (c) 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. + + +from cxmanage import get_tftp, get_nodes, get_node_strings, run_command + + +def node_fru_version_command(args): + """Get the node FRU version for each node. """ + tftp = get_tftp(args) + nodes = get_nodes(args, tftp) + results, errors = run_command(args, nodes, 'get_node_fru_version') + + # Print results if we were successful + if results: + node_strings = get_node_strings(args, results, justify=True) + for node in nodes: + print("%s: %s" % (node_strings[node], results[node])) + + print("") # For readability + + if not args.quiet and errors: + print('Some errors occured during the command.\n') + + +def slot_fru_version_command(args): + """Get the slot FRU version for each node. """ + tftp = get_tftp(args) + nodes = get_nodes(args, tftp) + results, errors = run_command(args, nodes, 'get_slot_fru_version') + + # Print results if we were successful + if results: + node_strings = get_node_strings(args, results, justify=True) + for node in nodes: + print("%s: %s" % (node_strings[node], results[node])) + + print("") # For readability + + if not args.quiet and errors: + print('Some errors occured during the command.\n') diff --git a/cxmanage_api/fabric.py b/cxmanage_api/fabric.py index c9b633e..807df90 100644 --- a/cxmanage_api/fabric.py +++ b/cxmanage_api/fabric.py @@ -1068,6 +1068,44 @@ class Fabric(object): """ return self._run_on_all_nodes(async, "get_depth_chart") + def get_node_fru_version(self, async=False): + """Get each node's node FRU version. + + >>> fabric.get_node_fru_version() + {0: 'bf7b471716113d5b9c47c6a5dd25f7a83f5c235c', + 1: 'bf7b471716113d5b9c47c6a5dd25f7a83f5c235c', + 2: 'bf7b471716113d5b9c47c6a5dd25f7a83f5c235c', + 3: 'bf7b471716113d5b9c47c6a5dd25f7a83f5c235c'} + + :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 node FRU versions for each node in the fabric + :rtype: dictionary + + """ + return self._run_on_all_nodes(async, "get_node_fru_version") + + def get_slot_fru_version(self, async=False): + """Get each node's slot FRU version. + + >>> fabric.get_slot_fru_version() + {0: 'bf7b471716113d5b9c47c6a5dd25f7a83f5c235c', + 1: 'bf7b471716113d5b9c47c6a5dd25f7a83f5c235c', + 2: 'bf7b471716113d5b9c47c6a5dd25f7a83f5c235c', + 3: 'bf7b471716113d5b9c47c6a5dd25f7a83f5c235c'} + + :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 slot FRU versions for each node in the fabric + :rtype: dictionary + + """ + return self._run_on_all_nodes(async, "get_slot_fru_version") + def _run_on_all_nodes(self, async, name, *args, **kwargs): """Start a command on all nodes.""" tasks = {} diff --git a/cxmanage_api/node.py b/cxmanage_api/node.py index 19066b6..e5d3eba 100644 --- a/cxmanage_api/node.py +++ b/cxmanage_api/node.py @@ -32,7 +32,6 @@ import os import re import time -import shutil import tempfile import subprocess @@ -1458,6 +1457,58 @@ class Node(object): """ return self.bmc.fabric_get_uplink_info().strip() + def get_node_fru_version(self): + """Get the node FRU version. + >>> node.get_node_fru_version + 'bf7b471716113d5b9c47c6a5dd25f7a83f5c235c' + + :return: The node FRU version of this node + :rtype: string + + This is essentially the equivalent of ipmitool FRU read 81 filename + and reading only the version from that file. + The in-file offset for the node FRU version is 516, and the + length of the version string is 40 bytes. + + """ + version = self._read_fru(81, offset=516, bytes_to_read=40) + # If there is an error reading the FRU, every byte could be x00 + if version == "\x00"*len(version): + raise Exception("No node FRU detected") + + # If the version string is less than 40 bytes long, remove the x00's + version = version.replace("\x00", "") + + return version + + def get_slot_fru_version(self): + """Get the slot FRU version. + >>> node.get_slot_fru_version + 'Unknown' + + :return: The slot FRU version of this node + :rtype: string + + This is essentially the equivalent of ipmitool FRU read 82 filename + and reading only the version from that file. + The in-file offset for the node FRU version is 516, and the + length of the version string is 40 bytes. + + Note that some system boards do not have slot FRUs, and are + therefore expected to raise an exception. + + """ + version = self._read_fru(82, offset=516, bytes_to_read=40) + # If there is an error reading the FRU, every byte could be x00 + if version == "\x00"*len(version): + raise Exception("No slot FRU detected. Perhaps the system " + \ + "board does not have slot FRUs?") + + # If the version string is less than 40 bytes long, remove the x00's + version = version.replace("\x00", "") + + return version + def _run_fabric_command(self, function_name, **kwargs): """Handles the basics of sending a node a command for fabric data.""" filename = temp_file() @@ -1689,5 +1740,16 @@ class Node(object): "Unable to increment SIMG priority, too high") return priority + def _read_fru(node, fru_number, offset=0, bytes_to_read=-1): + """Read from node's fru starting at offset. + This is equivalent to the ipmitool fru read command. + + """ + # Use a temporary file to store the FRU image + with tempfile.NamedTemporaryFile(delete=True) as hexfile: + node.bmc.fru_read(fru_number, hexfile.name) + hexfile.seek(offset) + return(hexfile.read(bytes_to_read)) + # End of file: ./node.py diff --git a/scripts/cxmanage b/scripts/cxmanage index ccde835..a3edb37 100755 --- a/scripts/cxmanage +++ b/scripts/cxmanage @@ -48,6 +48,8 @@ from cxmanage.commands.info import info_command from cxmanage.commands.ipmitool import ipmitool_command from cxmanage.commands.ipdiscover import ipdiscover_command from cxmanage.commands.tspackage import tspackage_command +from cxmanage.commands.fru_version import node_fru_version_command, \ + slot_fru_version_command PYIPMI_VERSION = '0.8.0' @@ -292,10 +294,21 @@ def build_parser(): parser.add_argument('hostname', help='nodes to operate on (see examples below)') - # tspackage command - tspackage = subparsers.add_parser('tspackage', help='Get all data from each node') + # tspackage command ("troubleshoot package") + tspackage = subparsers.add_parser('tspackage', + help='Save information about this node/fabric to a .tar') tspackage.set_defaults(func=tspackage_command) + # node_fru_version command + node_fru_version = subparsers.add_parser('node_fru_version', + help='Get the node FRU version') + node_fru_version.set_defaults(func=node_fru_version_command) + + # slot_fru_version command + slot_fru_version = subparsers.add_parser('slot_fru_version', + help='Get the slot FRU version') + slot_fru_version.set_defaults(func=slot_fru_version_command) + return parser |