summaryrefslogtreecommitdiff
path: root/cxmanage/commands
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2013-05-09 18:09:52 +0000
committer <>2013-06-05 18:25:08 +0000
commit7e1dea01472ce20c90dd69f11e6c07df9f1d6847 (patch)
tree51f11e81e42ba5d1b2e91b06a52bef5b45b0e460 /cxmanage/commands
downloadcxmanage-tarball-7e1dea01472ce20c90dd69f11e6c07df9f1d6847.tar.gz
Imported from /home/lorry/working-area/delta_cxmanage-tarball/cxmanage-0.8.2.tar.gz.HEADcxmanage-0.8.2masterbaserock/morph
Diffstat (limited to 'cxmanage/commands')
-rw-r--r--cxmanage/commands/__init__.py29
-rw-r--r--cxmanage/commands/config.py94
-rw-r--r--cxmanage/commands/fabric.py80
-rw-r--r--cxmanage/commands/fw.py164
-rw-r--r--cxmanage/commands/info.py103
-rw-r--r--cxmanage/commands/ipdiscover.py56
-rw-r--r--cxmanage/commands/ipmitool.py60
-rw-r--r--cxmanage/commands/mc.py47
-rw-r--r--cxmanage/commands/power.py110
-rw-r--r--cxmanage/commands/sensor.py83
10 files changed, 826 insertions, 0 deletions
diff --git a/cxmanage/commands/__init__.py b/cxmanage/commands/__init__.py
new file mode 100644
index 0000000..2160043
--- /dev/null
+++ b/cxmanage/commands/__init__.py
@@ -0,0 +1,29 @@
+# Copyright (c) 2012, 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.
diff --git a/cxmanage/commands/config.py b/cxmanage/commands/config.py
new file mode 100644
index 0000000..ca80928
--- /dev/null
+++ b/cxmanage/commands/config.py
@@ -0,0 +1,94 @@
+# Copyright (c) 2012, 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
+
+from cxmanage_api.ubootenv import UbootEnv, validate_boot_args
+
+
+def config_reset_command(args):
+ """reset to factory default settings"""
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp, verify_prompt=True)
+
+ if not args.quiet:
+ print "Sending config reset command..."
+
+ results, errors = run_command(args, nodes, "config_reset")
+
+ if not args.quiet and not errors:
+ print "Command completed successfully.\n"
+
+ return len(errors) > 0
+
+
+def config_boot_command(args):
+ """set A9 boot order"""
+ if args.boot_order == ['status']:
+ return config_boot_status_command(args)
+
+ validate_boot_args(args.boot_order)
+
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print "Setting boot order..."
+
+ results, errors = run_command(args, nodes, "set_boot_order",
+ args.boot_order)
+
+ if not args.quiet and not errors:
+ print "Command completed successfully.\n"
+
+ return len(errors) > 0
+
+
+def config_boot_status_command(args):
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print "Getting boot order..."
+ results, errors = run_command(args, nodes, "get_boot_order")
+
+ # Print results
+ if results:
+ node_strings = get_node_strings(args, results, justify=True)
+ print "Boot order"
+ for node in nodes:
+ if node in results:
+ print "%s: %s" % (node_strings[node], ",".join(results[node]))
+ print
+
+ if not args.quiet and errors:
+ print "Some errors occured during the command.\n"
+
+ return len(errors) > 0
diff --git a/cxmanage/commands/fabric.py b/cxmanage/commands/fabric.py
new file mode 100644
index 0000000..3bf84c2
--- /dev/null
+++ b/cxmanage/commands/fabric.py
@@ -0,0 +1,80 @@
+# Copyright (c) 2012, 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, run_command
+
+
+def ipinfo_command(args):
+ """get ip info from a cluster or host"""
+ args.all_nodes = False
+
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print "Getting IP addresses..."
+
+ results, errors = run_command(args, nodes, "get_fabric_ipinfo")
+
+ for node in nodes:
+ if node in results:
+ print 'IP info from %s' % node.ip_address
+ for node_id, node_address in results[node].iteritems():
+ print 'Node %i: %s' % (node_id, node_address)
+ print
+
+ return 0
+
+
+def macaddrs_command(args):
+ """get mac addresses from a cluster or host"""
+ args.all_nodes = False
+
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print "Getting MAC addresses..."
+ results, errors = run_command(args, nodes, "get_fabric_macaddrs")
+
+ for node in nodes:
+ if node in results:
+ print "MAC addresses from %s" % node.ip_address
+ for node_id in results[node]:
+ for port in results[node][node_id]:
+ for mac_address in results[node][node_id][port]:
+ print "Node %i, Port %i: %s" % (node_id, port,
+ mac_address)
+ print
+
+ if not args.quiet and errors:
+ print "Some errors occured during the command.\n"
+
+ return len(errors) == 0
diff --git a/cxmanage/commands/fw.py b/cxmanage/commands/fw.py
new file mode 100644
index 0000000..87f810b
--- /dev/null
+++ b/cxmanage/commands/fw.py
@@ -0,0 +1,164 @@
+# Copyright (c) 2012, 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 pkg_resources import parse_version
+
+from cxmanage import get_tftp, get_nodes, get_node_strings, run_command, \
+ prompt_yes
+
+from cxmanage_api.image import Image
+from cxmanage_api.firmware_package import FirmwarePackage
+
+
+def fwupdate_command(args):
+ """update firmware on a cluster or host"""
+ def do_update():
+ """ Do a single firmware check+update. Returns True on failure. """
+ if not args.force:
+ if not args.quiet:
+ print "Checking hosts..."
+
+ results, errors = run_command(args, nodes, "_check_firmware",
+ package, args.partition, args.priority)
+ if errors:
+ print "ERROR: Firmware update aborted."
+ return True
+
+ if not args.quiet:
+ print "Updating firmware..."
+
+ results, errors = run_command(args, nodes, "update_firmware", package,
+ args.partition, args.priority)
+ if errors:
+ print "ERROR: Firmware update failed."
+ return True
+
+ return False
+
+ def do_reset():
+ """ Reset and wait. Returns True on failure. """
+ if not args.quiet:
+ print "Checking ECME versions..."
+
+ results, errors = run_command(args, nodes, "get_versions")
+ if errors:
+ print "ERROR: MC reset aborted. Backup partitions not updated."
+ return True
+
+ for result in results.values():
+ version = result.ecme_version.lstrip("v")
+ if parse_version(version) < parse_version("1.2.0"):
+ print "ERROR: MC reset is unsafe on ECME version v%s" % version
+ print "Please power cycle the system and start a new fwupdate."
+ return True
+
+ if not args.quiet:
+ print "Resetting nodes..."
+
+ results, errors = run_command(args, nodes, "mc_reset", True)
+ if errors:
+ print "ERROR: MC reset failed. Backup partitions not updated."
+ return True
+
+ return False
+
+ if args.image_type == "PACKAGE":
+ package = FirmwarePackage(args.filename)
+ else:
+ try:
+ simg = None
+ if args.force_simg:
+ simg = False
+ elif args.skip_simg:
+ simg = True
+
+ image = Image(args.filename, args.image_type, simg, args.daddr,
+ args.skip_crc32, args.fw_version)
+ package = FirmwarePackage()
+ package.images.append(image)
+ except ValueError as e:
+ print "ERROR: %s" % e
+ return True
+
+ if not args.all_nodes:
+ if args.force:
+ print 'WARNING: Updating firmware without --all-nodes is dangerous.'
+ else:
+ if not prompt_yes(
+ 'WARNING: Updating firmware without --all-nodes is dangerous. Continue?'):
+ return 1
+
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp, verify_prompt=True)
+
+ errors = do_update()
+
+ if args.full and not errors:
+ errors = do_reset()
+ if not errors:
+ errors = do_update()
+
+ if not args.quiet and not errors:
+ print "Command completed successfully.\n"
+
+ return errors
+
+
+def fwinfo_command(args):
+ """print firmware info"""
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print "Getting firmware info..."
+
+ results, errors = run_command(args, nodes, "get_firmware_info")
+
+ node_strings = get_node_strings(args, results, justify=False)
+ for node in nodes:
+ if node in results:
+ print "[ Firmware info for %s ]" % node_strings[node]
+
+ for partition in results[node]:
+ print "Partition : %s" % partition.partition
+ print "Type : %s" % partition.type
+ print "Offset : %s" % partition.offset
+ print "Size : %s" % partition.size
+ print "Priority : %s" % partition.priority
+ print "Daddr : %s" % partition.daddr
+ print "Flags : %s" % partition.flags
+ print "Version : %s" % partition.version
+ print "In Use : %s" % partition.in_use
+ print
+
+ if not args.quiet and errors:
+ print "Some errors occured during the command.\n"
+
+ return len(errors) > 0
diff --git a/cxmanage/commands/info.py b/cxmanage/commands/info.py
new file mode 100644
index 0000000..d002906
--- /dev/null
+++ b/cxmanage/commands/info.py
@@ -0,0 +1,103 @@
+# Copyright (c) 2012, 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 info_command(args):
+ """print info from a cluster or host"""
+ if args.info_type in [None, 'basic']:
+ return info_basic_command(args)
+ elif args.info_type == 'ubootenv':
+ return info_ubootenv_command(args)
+
+
+def info_basic_command(args):
+ """Print basic info"""
+ components = [
+ ("ecme_version", "ECME version"),
+ ("cdb_version", "CDB version"),
+ ("stage2_version", "Stage2boot version"),
+ ("bootlog_version", "Bootlog version"),
+ ("a9boot_version", "A9boot version"),
+ ("uboot_version", "Uboot version"),
+ ("ubootenv_version", "Ubootenv version"),
+ ("dtb_version", "DTB version")
+ ]
+
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print "Getting info..."
+ results, errors = run_command(args, nodes, "get_versions")
+
+ # Print results
+ node_strings = get_node_strings(args, results, justify=False)
+ for node in nodes:
+ if node in results:
+ result = results[node]
+ print "[ Info from %s ]" % node_strings[node]
+ print "Hardware version : %s" % result.hardware_version
+ print "Firmware version : %s" % result.firmware_version
+ for var, string in components:
+ if hasattr(result, var):
+ version = getattr(result, var)
+ print "%s: %s" % (string.ljust(19), version)
+ print
+
+ if not args.quiet and errors:
+ print "Some errors occured during the command.\n"
+
+ return len(errors) > 0
+
+
+def info_ubootenv_command(args):
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print "Getting u-boot environment..."
+ results, errors = run_command(args, nodes, "get_ubootenv")
+
+ # Print results
+ node_strings = get_node_strings(args, results, justify=False)
+ for node in nodes:
+ if node in results:
+ ubootenv = results[node]
+ print "[ U-Boot Environment from %s ]" % node_strings[node]
+ for variable in ubootenv.variables:
+ print "%s=%s" % (variable, ubootenv.variables[variable])
+ print
+
+ if not args.quiet and errors:
+ print "Some errors occured during the command.\n"
+
+ return len(errors) > 0
diff --git a/cxmanage/commands/ipdiscover.py b/cxmanage/commands/ipdiscover.py
new file mode 100644
index 0000000..f619d16
--- /dev/null
+++ b/cxmanage/commands/ipdiscover.py
@@ -0,0 +1,56 @@
+# Copyright (c) 2012, 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 ipdiscover_command(args):
+ """discover server IP addresses"""
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print 'Getting server-side IP addresses...'
+
+ results, errors = run_command(args, nodes, 'get_server_ip', args.interface,
+ args.ipv6, args.server_user, args.server_password, args.aggressive)
+
+ if results:
+ node_strings = get_node_strings(args, results, justify=True)
+ print 'IP addresses (ECME, Server)'
+ for node in nodes:
+ if node in results:
+ print '%s: %s' % (node_strings[node], results[node])
+ print
+
+ if not args.quiet and errors:
+ print 'Some errors occurred during the command.'
+
+ return len(errors) > 0
diff --git a/cxmanage/commands/ipmitool.py b/cxmanage/commands/ipmitool.py
new file mode 100644
index 0000000..f8baf80
--- /dev/null
+++ b/cxmanage/commands/ipmitool.py
@@ -0,0 +1,60 @@
+# Copyright (c) 2012, 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 ipmitool_command(args):
+ """run arbitrary ipmitool command"""
+ if args.lanplus:
+ ipmitool_args = ['-I', 'lanplus'] + args.ipmitool_args
+ else:
+ ipmitool_args = args.ipmitool_args
+
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print "Running IPMItool command..."
+ results, errors = run_command(args, nodes, "ipmitool_command",
+ ipmitool_args)
+
+ # Print results
+ node_strings = get_node_strings(args, results, justify=False)
+ for node in nodes:
+ if node in results and results[node] != "":
+ print "[ IPMItool output from %s ]" % node_strings[node]
+ print results[node]
+ print
+
+ if not args.quiet and errors:
+ print "Some errors occured during the command.\n"
+
+ return len(errors) > 0
diff --git a/cxmanage/commands/mc.py b/cxmanage/commands/mc.py
new file mode 100644
index 0000000..2573540
--- /dev/null
+++ b/cxmanage/commands/mc.py
@@ -0,0 +1,47 @@
+# Copyright (c) 2012, 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, run_command
+
+
+def mcreset_command(args):
+ """reset the management controllers of a cluster or host"""
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print 'Sending MC reset command...'
+
+ results, errors = run_command(args, nodes, 'mc_reset')
+
+ if not args.quiet and not errors:
+ print 'Command completed successfully.\n'
+
+ return len(errors) > 0
diff --git a/cxmanage/commands/power.py b/cxmanage/commands/power.py
new file mode 100644
index 0000000..b5b6015
--- /dev/null
+++ b/cxmanage/commands/power.py
@@ -0,0 +1,110 @@
+# Copyright (c) 2012, 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 power_command(args):
+ """change the power state of a cluster or host"""
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print 'Sending power %s command...' % args.power_mode
+
+ results, errors = run_command(args, nodes, 'set_power', args.power_mode)
+
+ if not args.quiet and not errors:
+ print 'Command completed successfully.\n'
+
+ return len(errors) > 0
+
+
+def power_status_command(args):
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print 'Getting power status...'
+ results, errors = run_command(args, nodes, 'get_power')
+
+ # Print results
+ if results:
+ node_strings = get_node_strings(args, results, justify=True)
+ print 'Power status'
+ for node in nodes:
+ if node in results:
+ result = 'on' if results[node] else 'off'
+ print '%s: %s' % (node_strings[node], result)
+ print
+
+ if not args.quiet and errors:
+ print 'Some errors occured during the command.\n'
+
+ return len(errors) > 0
+
+
+def power_policy_command(args):
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print 'Setting power policy to %s...' % args.policy
+
+ results, errors = run_command(args, nodes, 'set_power_policy',
+ args.policy)
+
+ if not args.quiet and not errors:
+ print 'Command completed successfully.\n'
+
+ return len(errors) > 0
+
+
+def power_policy_status_command(args):
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print 'Getting power policy status...'
+ results, errors = run_command(args, nodes, 'get_power_policy')
+
+ # Print results
+ if results:
+ node_strings = get_node_strings(args, results, justify=True)
+ print 'Power policy status'
+ for node in nodes:
+ if node in results:
+ print '%s: %s' % (node_strings[node], results[node])
+ print
+
+ if not args.quiet and errors:
+ print 'Some errors occured during the command.\n'
+
+ return len(errors) > 0
diff --git a/cxmanage/commands/sensor.py b/cxmanage/commands/sensor.py
new file mode 100644
index 0000000..c3fed32
--- /dev/null
+++ b/cxmanage/commands/sensor.py
@@ -0,0 +1,83 @@
+# Copyright (c) 2012, 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 sensor_command(args):
+ """read sensor values from a cluster or host"""
+ tftp = get_tftp(args)
+ nodes = get_nodes(args, tftp)
+
+ if not args.quiet:
+ print "Getting sensor readings..."
+ results, errors = run_command(args, nodes, "get_sensors",
+ args.sensor_name)
+
+ sensors = {}
+ for node in nodes:
+ if node in results:
+ for sensor_name, sensor in results[node].iteritems():
+ if not sensor_name in sensors:
+ sensors[sensor_name] = []
+
+ reading = sensor.sensor_reading.replace("(+/- 0) ", "")
+ try:
+ value = float(reading.split()[0])
+ suffix = reading.lstrip("%f " % value)
+ sensors[sensor_name].append((node, value, suffix))
+ except ValueError:
+ sensors[sensor_name].append((node, reading, ""))
+
+ node_strings = get_node_strings(args, results, justify=True)
+ jsize = len(node_strings.itervalues().next())
+ for sensor_name, readings in sensors.iteritems():
+ print sensor_name
+
+ for node, reading, suffix in readings:
+ print "%s: %.2f %s" % (node_strings[node], reading, suffix)
+
+ try:
+ if all(suffix == x[2] for x in readings):
+ minimum = min(x[1] for x in readings)
+ maximum = max(x[1] for x in readings)
+ average = sum(x[1] for x in readings) / len(readings)
+ print "%s: %.2f %s" % ("Minimum".ljust(jsize), minimum, suffix)
+ print "%s: %.2f %s" % ("Maximum".ljust(jsize), maximum, suffix)
+ print "%s: %.2f %s" % ("Average".ljust(jsize), average, suffix)
+ except ValueError:
+ pass
+
+ print
+
+ if not args.quiet and errors:
+ print "Some errors occured during the command.\n"
+
+ return len(errors) > 0