summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSheldon Sandbekkhaug <sheldon.sandbekkhaug@calxeda.com>2013-08-09 15:20:06 -0500
committerSheldon Sandbekkhaug <sheldon.sandbekkhaug@calxeda.com>2013-08-09 15:20:06 -0500
commitff798ac2b2952d8340fcc40158a103dbc38a740f (patch)
treea6a853be145ec34f16a414b5d55ef344d728ae79
parent7adb81f7cc7b443c011a764f60ec0c8913d476b7 (diff)
downloadcxmanage-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.py69
-rw-r--r--cxmanage_api/fabric.py38
-rw-r--r--cxmanage_api/node.py64
-rwxr-xr-xscripts/cxmanage17
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