summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--PKG-INFO12
-rw-r--r--README5
-rw-r--r--pyipmi.egg-info/PKG-INFO12
-rw-r--r--pyipmi.egg-info/SOURCES.txt62
-rw-r--r--pyipmi.egg-info/dependency_links.txt1
-rw-r--r--pyipmi.egg-info/requires.txt1
-rw-r--r--pyipmi.egg-info/top_level.txt1
-rw-r--r--pyipmi/__init__.py213
-rw-r--r--pyipmi/bmc.py567
-rw-r--r--pyipmi/bootdev.py40
-rw-r--r--pyipmi/bootparam.py35
-rw-r--r--pyipmi/channel.py51
-rw-r--r--pyipmi/chassis.py118
-rw-r--r--pyipmi/commands/__init__.py89
-rw-r--r--pyipmi/commands/bmc.py152
-rw-r--r--pyipmi/commands/bootdev.py80
-rw-r--r--pyipmi/commands/bootparam.py66
-rw-r--r--pyipmi/commands/channel.py201
-rw-r--r--pyipmi/commands/chassis.py80
-rw-r--r--pyipmi/commands/data.py125
-rw-r--r--pyipmi/commands/dcmi.py337
-rw-r--r--pyipmi/commands/event.py79
-rw-r--r--pyipmi/commands/fabric.py306
-rw-r--r--pyipmi/commands/fabric_config.py288
-rw-r--r--pyipmi/commands/freeipmi_pef.py211
-rw-r--r--pyipmi/commands/fru.py132
-rw-r--r--pyipmi/commands/fw.py400
-rw-r--r--pyipmi/commands/info.py92
-rw-r--r--pyipmi/commands/lan.py127
-rw-r--r--pyipmi/commands/mc.py69
-rw-r--r--pyipmi/commands/payload.py65
-rw-r--r--pyipmi/commands/pef.py130
-rw-r--r--pyipmi/commands/pet.py55
-rw-r--r--pyipmi/commands/sdr.py110
-rw-r--r--pyipmi/commands/sel.py229
-rw-r--r--pyipmi/commands/sol.py207
-rw-r--r--pyipmi/commands/user.py163
-rw-r--r--pyipmi/commands/watchdog.py92
-rw-r--r--pyipmi/data.py48
-rw-r--r--pyipmi/dcmi.py92
-rw-r--r--pyipmi/event.py41
-rw-r--r--pyipmi/fabric.py63
-rw-r--r--pyipmi/freeipmi_pef.py56
-rw-r--r--pyipmi/fru.py56
-rw-r--r--pyipmi/fw.py115
-rw-r--r--pyipmi/info.py41
-rw-r--r--pyipmi/lan.py41
-rw-r--r--pyipmi/mc.py36
-rw-r--r--pyipmi/pef.py52
-rw-r--r--pyipmi/pet.py36
-rw-r--r--pyipmi/sdr.py49
-rw-r--r--pyipmi/sel.py171
-rw-r--r--pyipmi/server.py76
-rw-r--r--pyipmi/sol.py228
-rw-r--r--pyipmi/tools/__init__.py35
-rw-r--r--pyipmi/tools/ipmi_pef_config.py126
-rw-r--r--pyipmi/tools/ipmi_pet.py126
-rw-r--r--pyipmi/tools/ipmidcmi.py126
-rw-r--r--pyipmi/tools/ipmitool.py111
-rw-r--r--pyipmi/tools/responseparser.py269
-rw-r--r--pyipmi/user.py57
-rw-r--r--pyipmi/watchdog.py46
-rw-r--r--setup.cfg5
-rw-r--r--setup.py45
64 files changed, 7150 insertions, 0 deletions
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..d629258
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,12 @@
+Metadata-Version: 1.1
+Name: pyipmi
+Version: 0.7.1
+Summary: Wrapper for IPMI clients
+Home-page: UNKNOWN
+Author: Calxeda
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python :: 2.7
diff --git a/README b/README
new file mode 100644
index 0000000..527cd22
--- /dev/null
+++ b/README
@@ -0,0 +1,5 @@
+Python library that provides IPMI commands.
+
+The goal is to provide an abstraction layer above tools that provide
+IPMI (ipmitool, freeipmi) so code can be written once and reused across
+different tools.
diff --git a/pyipmi.egg-info/PKG-INFO b/pyipmi.egg-info/PKG-INFO
new file mode 100644
index 0000000..d629258
--- /dev/null
+++ b/pyipmi.egg-info/PKG-INFO
@@ -0,0 +1,12 @@
+Metadata-Version: 1.1
+Name: pyipmi
+Version: 0.7.1
+Summary: Wrapper for IPMI clients
+Home-page: UNKNOWN
+Author: Calxeda
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python :: 2.7
diff --git a/pyipmi.egg-info/SOURCES.txt b/pyipmi.egg-info/SOURCES.txt
new file mode 100644
index 0000000..3e05734
--- /dev/null
+++ b/pyipmi.egg-info/SOURCES.txt
@@ -0,0 +1,62 @@
+README
+setup.py
+pyipmi/__init__.py
+pyipmi/bmc.py
+pyipmi/bootdev.py
+pyipmi/bootparam.py
+pyipmi/channel.py
+pyipmi/chassis.py
+pyipmi/data.py
+pyipmi/dcmi.py
+pyipmi/event.py
+pyipmi/fabric.py
+pyipmi/freeipmi_pef.py
+pyipmi/fru.py
+pyipmi/fw.py
+pyipmi/info.py
+pyipmi/lan.py
+pyipmi/mc.py
+pyipmi/pef.py
+pyipmi/pet.py
+pyipmi/sdr.py
+pyipmi/sel.py
+pyipmi/server.py
+pyipmi/sol.py
+pyipmi/user.py
+pyipmi/watchdog.py
+pyipmi.egg-info/PKG-INFO
+pyipmi.egg-info/SOURCES.txt
+pyipmi.egg-info/dependency_links.txt
+pyipmi.egg-info/requires.txt
+pyipmi.egg-info/top_level.txt
+pyipmi/commands/__init__.py
+pyipmi/commands/bmc.py
+pyipmi/commands/bootdev.py
+pyipmi/commands/bootparam.py
+pyipmi/commands/channel.py
+pyipmi/commands/chassis.py
+pyipmi/commands/data.py
+pyipmi/commands/dcmi.py
+pyipmi/commands/event.py
+pyipmi/commands/fabric.py
+pyipmi/commands/fabric_config.py
+pyipmi/commands/freeipmi_pef.py
+pyipmi/commands/fru.py
+pyipmi/commands/fw.py
+pyipmi/commands/info.py
+pyipmi/commands/lan.py
+pyipmi/commands/mc.py
+pyipmi/commands/payload.py
+pyipmi/commands/pef.py
+pyipmi/commands/pet.py
+pyipmi/commands/sdr.py
+pyipmi/commands/sel.py
+pyipmi/commands/sol.py
+pyipmi/commands/user.py
+pyipmi/commands/watchdog.py
+pyipmi/tools/__init__.py
+pyipmi/tools/ipmi_pef_config.py
+pyipmi/tools/ipmi_pet.py
+pyipmi/tools/ipmidcmi.py
+pyipmi/tools/ipmitool.py
+pyipmi/tools/responseparser.py \ No newline at end of file
diff --git a/pyipmi.egg-info/dependency_links.txt b/pyipmi.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/pyipmi.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/pyipmi.egg-info/requires.txt b/pyipmi.egg-info/requires.txt
new file mode 100644
index 0000000..389a6c3
--- /dev/null
+++ b/pyipmi.egg-info/requires.txt
@@ -0,0 +1 @@
+pexpect \ No newline at end of file
diff --git a/pyipmi.egg-info/top_level.txt b/pyipmi.egg-info/top_level.txt
new file mode 100644
index 0000000..c655954
--- /dev/null
+++ b/pyipmi.egg-info/top_level.txt
@@ -0,0 +1 @@
+pyipmi
diff --git a/pyipmi/__init__.py b/pyipmi/__init__.py
new file mode 100644
index 0000000..ef6c175
--- /dev/null
+++ b/pyipmi/__init__.py
@@ -0,0 +1,213 @@
+# 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.
+
+
+"""pyipmi provides IPMI client functionality"""
+from __future__ import print_function
+
+__all__ = ['Handle', 'Tool', 'Command', 'make_bmc', 'IpmiError']
+
+class Handle:
+ """A handle to speak with a BMC
+
+ Handles use a Tool to speak with a BMC. It's basically a session handle
+ from its user's perspective, although handles may or may not use a single
+ ipmi session for their duration, depending on their implementation.
+
+ The Handle class itself is concrete, but may become abstract in the future.
+ """
+
+ def __init__(self, bmc, tool_class, command_list):
+ """
+ Arguments:
+ bmc -- A BMC object
+ tool_class -- the class of the tool to be used for this handle,
+ for example, IpmiTool.
+ command_list -- a list of Commands to be made available to this handle.
+ """
+ self.bmc = bmc
+ self._tool = tool_class(self, command_list)
+ self._add_command_stubs(command_list)
+ self._log_file = None
+
+ def _add_command_stubs(self, command_list):
+ """Adds command methods to an instance of Handle
+
+ Each command in the command_list supplied to init will add a method to
+ this handle instance. Calling that method causes the command to be issued.
+ """
+ for command in command_list:
+ self._add_command_stub(command)
+
+ def _add_command_stub(self, command):
+ """Add a a method for a command"""
+ def _cmd(*args, **kwargs):
+ """Call the method of the same name on the tool"""
+ tool_method = getattr(self._tool, command)
+ return tool_method(*args, **kwargs)
+
+ setattr(self, command, _cmd)
+
+ def set_log(self, log_file):
+ """Setup a logger for the handle
+
+ Arguments:
+ log_file -- a file like object
+ """
+ self._log_file = log_file
+
+ def set_verbose(self, verbose):
+ """Set verbosity for the handle
+
+ Arguments:
+ verbose -- true or false
+ """
+ self._verbose = verbose
+
+ def log(self, string):
+ """Write a string to a log
+
+ The log is flushed after log is written
+
+ Arguments:
+ string -- the string to log."""
+ if len(string) > 0:
+ if (self._log_file):
+ print(string, file = self._log_file)
+ self._log_file.flush()
+ if (self._verbose):
+ print(string)
+
+class Tool(object):
+ """A tool implements communications with a BMC
+
+ Tool is an abstract class - it needs a 'run' method defined to be useful.
+
+ Tool implementations vary in the way they implement IPMI communications.
+ The IpmiTool implementation uses high level ipmitool commands executed via
+ subprocesses. A freeipmi implementation could do the same using freeipmi
+ commands, or there could be a RawIpmiTool implementation that used IpmiTool
+ with raw commands. Another possibility is implementing IPMI natively in
+ python, and having a NativeIpmi Tool implementation for that.
+
+ Tool instances are bound to handle instances - each tool has exactly one
+ handle.
+
+ Tool instances are created with a list of commands - each command in the
+ list causes a method (named after the command) to be added to the tool
+ for executing the command. Commands in the list must implement support
+ for the tool - each tool has
+
+ Concrete implementations should go in the tools directory. An example
+ concrete implementation is the ImpiTool class.
+ """
+ def __init__(self, handle, command_list):
+ """
+ Arguments:
+ handle -- the handle to which this command is bound
+ command_list -- the list of commands the tool can execute
+ """
+ self._handle = handle
+ self._add_command_stubs(command_list)
+ self._command_list = command_list
+
+ def _add_command_stubs(self, command_list):
+ """Add command methods to this Tool instance
+
+ Just like handles, tools get a method per command in command_list
+ """
+ for command in command_list:
+ self._add_command_stub(command)
+
+ def _add_command_stub(self, command):
+ """Add an individual command method"""
+ def _cmd(*args, **kwargs):
+ """An individual command method.
+
+ Uses this tool's run method to execute a command in this
+ tool's special way."""
+ inst = self._command_list[command](self, *args, **kwargs)
+ return self.run(inst)
+
+ setattr(self, command, _cmd)
+
+ def _log(self, string):
+ """Log a message via this tool's handle"""
+ self._handle.log(string)
+
+ def run(self, command):
+ """This should be defined in a subclass of Tool"""
+ pass
+
+class Command:
+ """A Command describes a specific IPMI command"""
+ def __init__(self, tool, **params):
+ self._tool = tool
+ self._params = params
+
+class InteractiveCommand(Command):
+ """A dummy class for an interactive command"""
+
+def make_bmc(bmc_class, logfile = None, verbose = True, **kwargs):
+ """Returns a bmc object with 'default' settings
+
+ This uses IpmiTool for the tool,the base Handle class, and
+ the default "ipmi_commands" list of IPMI commands.
+
+ kwargs is combined with those default settings into a single
+ dict with its contents passed as keyword args when calling
+ bmc_class.
+
+ Arguments:
+ bmc_class -- called w/ kwargs as its parameter to get the
+ object to return.
+
+ Keyword arguments:
+ logfile -- an optional file object for logging (default none)
+ """
+
+ from commands import ipmi_commands
+ from tools import IpmiTool
+ bmc_kwargs = {
+ 'tool_class' : IpmiTool,
+ 'handle_class' : Handle,
+ 'command_list' : ipmi_commands
+ }
+
+ bmc_kwargs.update(kwargs)
+ bmc_obj = bmc_class(**bmc_kwargs)
+ bmc_obj.handle.set_log(logfile)
+ bmc_obj.handle.set_verbose(verbose)
+
+ return bmc_obj
+
+
+class IpmiError(Exception):
+ """A wrapper for an Ipmi error"""
diff --git a/pyipmi/bmc.py b/pyipmi/bmc.py
new file mode 100644
index 0000000..cda3c9b
--- /dev/null
+++ b/pyipmi/bmc.py
@@ -0,0 +1,567 @@
+# 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.
+
+
+"""Stuff about BMC's"""
+
+__all__ = ['BMC', 'BMCInfo', 'BMCGuid', 'BMCEnables', 'LanBMC']
+
+class BMCResult(object):
+ """superclass for BMC result objects
+
+ Sets attribute names from a dict passed to init, and allows comparison
+ with other results based on dict equality.
+ """
+ def __init__(self, **entries):
+ """each kwarg's value to an attribute of the same name"""
+ self.__dict__.update(entries)
+
+ def __eq__(self, other):
+ """BMCInfo's are equivalent if their attributes are the same"""
+ return self.__dict__ == other.__dict__
+
+
+class BMCInfo(BMCResult):
+ """Describes a BMC info record result
+
+ "bmc info" is just the ipmitool name - this is really "get device id"
+ """
+ device_id = None
+ device_revision = None
+ firmware_revision = None
+ ipmi_version = None
+ is_chassis_device = None
+ is_bridge = None
+ is_ipmb_event_generator = None
+ is_ipmb_event_receiver = None
+ is_fru_inventory_device = None
+ is_sel_device = None
+ is_sdr_repository_device = None
+ is_sensor_device = None
+ manufacturer_id = None
+ product_id = None
+ device_available = None
+ aux_firmware_revision_info = None
+
+class BMCGuid(BMCResult):
+ """Result record for bmc guid command
+
+ this is really "get device guid" - bmc guid is the ipmitool command name.
+ """
+ system_guid = None
+ time_stamp = None
+
+class BMCEnables(BMCResult):
+ """Result record for get command enables
+
+ The names for the attributes here come from the command line syntax for ipmitool
+ """
+ recv_msg_intr = None
+ event_msg_intr = None
+ event_msg = None
+ system_event_log = None
+ oem0 = None
+ oem1 = None
+ oem2 = None
+
+class BMCResetResult(BMCResult):
+ """Result record for bmc reset command"""
+ pass
+
+class BMC(object):
+ """A BMC - what you're talking to when you're talking IPMI
+
+ I think this should ultimately be the interface for all commands issued
+ from IPMI.. or maybe just commands "about the bmc". This needs to be
+ resolved!
+ """
+ def __init__(self, handle_class, tool_class, command_list):
+ """
+ Arguments:
+ handle_class -- class to use for Handles created
+ tool_class -- class to use for the Tool for each handle
+ command_list -- the list of commands available for this BMC
+ """
+ self.handle = handle_class(self, tool_class, command_list)
+
+ def info(self):
+ """Get the BMC's info"""
+ return self.handle.get_device_id()
+
+ def selftest(self):
+ """Get BMC self test results"""
+ return self.handle.selftest()
+
+ def bmc_reset(self, type='warm'):
+ return self.handle.bmc_reset(type=type)
+
+ def guid(self):
+ """Get the BMC's guid"""
+ return self.handle.get_system_guid()
+
+ def sdr_list(self):
+ """Get a list of SDR's for the BMC"""
+ return self.handle.get_sdr_list()
+
+ def enables(self):
+ """Return a BMCEnables object for the BMC"""
+ return self.handle.get_command_enables()
+
+ def set_chassis_power(self, mode):
+ return self.handle.chassis_control(mode=mode)
+
+ def set_chassis_policy(self, state):
+ return self.handle.chassis_policy(state=state)
+
+ def get_chassis_status(self):
+ return self.handle.chassis_status()
+
+ def update_socman(self, filename, partition, tftp_addr):
+ return self.update_firmware(filename, partition, '3', tftp_addr)
+
+ def update_firmware(self, filename, partition, image_type, tftp_addr):
+ return self.handle.fw_download(filename=filename, partition=partition,
+ image_type=image_type,
+ tftp_addr=tftp_addr)
+
+
+ def retrieve_firmware(self, filename, partition, image_type, tftp_addr):
+ return self.handle.fw_upload(filename=filename, partition=partition,
+ image_type=image_type,
+ tftp_addr=tftp_addr)
+
+ def register_firmware_read(self, filename, partition, image_type):
+ return self.handle.fw_register_read(filename=filename,
+ partition=partition,
+ image_type=image_type)
+
+ def register_firmware_write(self, filename, partition, image_type):
+ return self.handle.fw_register_write(filename=filename,
+ partition=partition,
+ image_type=image_type)
+
+ def activate_firmware(self, partition):
+ return self.handle.fw_activate(partition=partition)
+
+ def invalidate_firmware(self, partition):
+ return self.handle.fw_invalidate(partition=partition)
+
+ def set_firmware_flags(self, partition, flags):
+ return self.handle.fw_flags(partition=partition, flags=flags)
+
+ def get_firmware_status(self, tftp_handle):
+ return self.handle.fw_status(tftp_handle=tftp_handle)
+
+ def check_firmware(self, partition):
+ return self.handle.fw_check(partition=partition)
+
+ def cancel_firmware(self, job_id):
+ return self.handle.fw_cancel(job_id=job_id)
+
+ def update_raw_firmware(self, filename, offset, size, tftp_addr):
+ return self.handle.fw_put(filename=filename, offset=offset,
+ size=size, tftp_addr=tftp_addr)
+
+ def retrieve_raw_firmware(self, filename, offset, size, tftp_addr):
+ return self.handle.fw_get(filename=filename, offset=offset,
+ size=size, tftp_addr=tftp_addr)
+
+ def get_firmware_info(self):
+ return self.handle.fw_info()
+
+ def reset_firmware(self):
+ return self.handle.fw_reset()
+
+ def set_firmware_version(self, version):
+ return self.handle.fw_version(version=version)
+
+ def get_sel_time(self):
+ """Get the time for the SEL"""
+ return self.handle.get_sel_time()
+
+ def set_sel_time(self, time):
+ """Set the time for the SEL"""
+ return self.handle.set_sel_time(time=time)
+
+ def sel_info(self):
+ """Get SEL info"""
+ return self.handle.sel_info()
+
+ def sel_alloc_info(self):
+ """Get SEL alloc info"""
+ return self.handle.sel_alloc_info()
+
+ def sel_add(self, *records):
+ """Add records to the SEL"""
+ return self.handle.sel_add(records=records)
+
+ def sel_get(self, *record_ids):
+ """Get SEL Records"""
+ return self.handle.sel_get(record_ids=record_ids)
+
+ def sel_list(self):
+ """List SEL entries"""
+ return self.handle.sel_list()
+
+ def sel_clear(self):
+ """Clear the SEL"""
+ return self.handle.sel_clear()
+
+ def set_sol_config_param(self, param, value):
+ """Set SOL Configuration Parameter"""
+ return self.handle.set_sol_config_params(param=param, value=value)
+
+ def get_sol_config_params(self, *params):
+ """Get SOL Configuration Parameters"""
+ return self.handle.get_sol_config_params(params=params)
+
+ def activate_payload(self):
+ """Activate an SOL session"""
+ return self.handle.activate_payload()
+
+ def deactivate_payload(self):
+ """Deactivate an SOL session"""
+ return self.handle.deactivate_payload()
+
+ def dcmi_get_capabilities(self):
+ return self.handle.dcmi_get_capabilities()
+
+ def dcmi_set_asset_tag(self, tag):
+ return self.handle.dcmi_set_asset_tag(tag=tag)
+
+ def dcmi_get_asset_tag(self):
+ return self.handle.dcmi_get_asset_tag()
+
+ def dcmi_get_controller_id(self):
+ return self.handle.dcmi_get_controller_id()
+
+ def dcmi_set_controller_id(self, controller):
+ return self.handle.dcmi_set_controller_id(controller=controller)
+
+ def dcmi_get_sensor_info(self):
+ return self.handle.dcmi_get_sensor_info()
+
+ def dcmi_get_power_statistics(self):
+ return self.handle.dcmi_get_power_statistics()
+
+ def dcmi_get_power_limit(self):
+ return self.handle.dcmi_get_power_limit()
+
+ def dcmi_set_power_limit(self):
+ return self.handle.dcmi_set_power_limit()
+
+ def dcmi_power_limit_requested(self, limit, exception=None):
+ return self.handle.dcmi_power_limit_requested(limit=limit, exception=exception)
+
+ def dcmi_correction_time_limit(self, time_limit, exception=None):
+ return self.handle.dcmi_correction_time_limit(time_limit=time_limit, exception=exception)
+
+ def dcmi_statistics_sampling_period(self, period, exception=None):
+ return self.handle.dcmi_statistics_sampling_period(period=period, exception=exception)
+
+
+ def dcmi_activate_power_limit(self, action):
+ if action != "activate" and action != "deactivate":
+ raise Exception("Invalid argument to dcmi_activate_power_limit: %s" % action)
+ return self.handle.dcmi_activate_power_limit(action=action)
+
+ def pef_get_info(self):
+ return self.handle.pef_get_info()
+
+ def pef_get_status(self):
+ return self.handle.pef_get_status()
+
+ def pef_get_policies(self):
+ return self.handle.pef_get_policies()
+
+ def pef_list_entries(self):
+ return self.handle.pef_list_entries()
+
+ def pef_config_get_info(self):
+ return self.handle.pef_config_info()
+
+ def pef_checkout(self, section=None, filename=None, key=None):
+ return self.handle.pef_checkout(section=section, filename=filename,
+ key=key)
+
+ def pef_commit(self, section=None, filename=None, key_value_pair=None):
+ return self.handle.pef_commit(section=section, filename=filename,
+ key_value_pair=key_value_pair)
+
+ def pef_diff(self, section=None, filename=None, key=None):
+ return self.handle.pef_diff(section=section, filename=filename,
+ key=key)
+
+ def pef_list_sections(self):
+ return self.handle.pef_list_sections()
+
+ def generate_generic_event(self, event_type):
+ return self.handle.generic_event(event_type=event_type)
+
+ def generate_sensor_event(self, sensor_id, state):
+ return self.handle.assert_sensor_event(sensor_id=sensor_id,
+ state=state)
+
+ def get_watchdog_status(self):
+ return self.handle.watchdog_get()
+
+ def reset_watchdog(self):
+ return self.handle.watchdog_reset()
+
+ def disable_watchdog(self):
+ return self.handle.watchdog_off()
+
+ def fru_get_inventory(self):
+ return self.handle.fru_print()
+
+ def fru_read(self, fru_id, filename):
+ return self.handle.fru_read(fru_id=fru_id, filename=filename)
+
+ def fru_write(self, fru_id, filename):
+ return self.handle.fru_write(fru_id=fru_id, filename=filename)
+
+ def fru_upg_e_key(self, fru_id, filename):
+ return self.handle.fru_upg_e_key(fru_id=fru_id, filename=filename)
+
+ def fru_show(self, filename):
+ return self.handle.fru_show(filename=filename)
+
+ def lan_print(self, channel=''):
+ return self.handle.lan_print(channel=channel)
+
+ def lan_set(self, channel, command, param):
+ return self.handle.lan_set(channel=channel, command=command,
+ param=param)
+
+ def channel_info(self):
+ return self.handle.channel_info()
+
+ def channel_get_access(self, channel, userid=""):
+ return self.handle.channel_get_access(channel=channel, userid=userid)
+
+ def channel_set_access(self, channel, userid, callin=None, ipmi=None,
+ link=None, priv_level=None):
+ return self.handle.channel_set_access(channel=channel, userid=userid,
+ callin=callin, ipmi=ipmi,
+ link=link, priv_level=priv_level)
+
+ def channel_get_ciphers(self, mode='ipmi'):
+ return self.handle.channel_get_ciphers(mode=mode)
+
+ def user_list(self, channel=None):
+ return self.handle.user_list(channel=channel)
+
+ def user_set_name(self, userid, name):
+ return self.handle.user_set_name(userid=userid, name=name)
+
+ def user_set_password(self, userid, password=None):
+ return self.handle.user_set_password(userid=userid, password=password)
+
+ def user_enable(self, userid):
+ return self.handle.user_enable(userid=userid)
+
+ def user_disable(self, userid):
+ return self.handle.user_disable(userid=userid)
+
+ def user_priv(self, userid, priv_level, channel=None):
+ return self.handle.user_priv(userid=userid, priv_level=priv_level,
+ channel=channel)
+ #
+ # fabric commands
+ #
+
+ def fabric_update_config(self):
+ return self.handle.fabric_updateconfig()
+
+ def fabric_get_node_id(self):
+ return self.handle.fabric_getnodeid()
+
+ def fabric_get_ip_addr(self, nodeid="", iface=""):
+ return self.handle.fabric_getipaddr(nodeid=nodeid, iface=iface)
+
+ def fabric_get_mac_addr(self, nodeid="", iface=0):
+ return self.handle.fabric_getmacaddr(nodeid=nodeid, iface=iface)
+
+ def fabric_get_linkspeed(self, link=None, actual=None):
+ return self.handle.fabric_getlinkspeed(link=link, actual=actual)
+
+ def fabric_get_linkstats(self, filename=None, tftp_addr=None, link=None):
+ return self.handle.fabric_getlinkstats(
+ filename=filename,
+ tftp_addr=tftp_addr,
+ link=link
+ )
+
+ def fabric_get_linkmap(self, filename, tftp_addr=None):
+ return self.handle.fabric_getlinkmap(filename=filename,
+ tftp_addr=tftp_addr)
+
+ def fabric_get_routingtable(self, filename, tftp_addr=None):
+ return self.handle.fabric_getroutingtable(filename=filename,
+ tftp_addr=tftp_addr)
+
+ def fabric_get_depthchart(self, filename, tftp_addr=None):
+ return self.handle.fabric_getdepthchart(filename=filename,
+ tftp_addr=tftp_addr)
+
+ def fabric_add_macaddr(self, iface, macaddr, nodeid=None):
+ return self.handle.fabric_addmacaddr(nodeid=nodeid, iface=iface, macaddr=macaddr)
+
+ def fabric_rm_macaddr(self, iface, macaddr, nodeid=None):
+ return self.handle.fabric_rmmacaddr(nodeid=nodeid, iface=iface, macaddr=macaddr)
+
+ #
+ # fabric config commands
+ #
+
+ def fabric_config_update_config(self):
+ return self.handle.fabric_config_updateconfig()
+
+ def fabric_config_get_ip_info(self, filename, tftp_addr=None):
+ return self.handle.fabric_config_getipinfo(filename=filename,
+ tftp_addr=tftp_addr)
+
+ def fabric_config_get_mac_addresses(self, filename, tftp_addr=None):
+ return self.handle.fabric_config_getmacaddresses(filename=filename,
+ tftp_addr=tftp_addr)
+
+ def fabric_config_get_uplink_info(self, filename, tftp_addr=None):
+ return self.handle.fabric_config_getuplinkinfo(filename=filename,
+ tftp_addr=tftp_addr)
+
+ def fabric_config_get_ip_src(self):
+ return self.handle.fabric_config_getipsrc()
+
+ def fabric_config_set_ip_src(self, ipsrc_mode):
+ return self.handle.fabric_config_setipsrc(ipsrc_mode=ipsrc_mode)
+
+ def fabric_config_factory_default(self):
+ return self.handle.fabric_config_factory_default()
+
+ def fabric_config_get_ip_addr_base(self):
+ return self.handle.fabric_config_get_ipaddr_base()
+
+ def fabric_config_get_linkspeed(self):
+ return self.handle.fabric_config_getlinkspeed()
+
+ def fabric_config_set_linkspeed(self, linkspeed):
+ return self.handle.fabric_config_setlinkspeed(linkspeed=linkspeed)
+
+ def fabric_config_get_linkspeed_policy(self):
+ return self.handle.fabric_config_getlinkspeedpolicy()
+
+ def fabric_config_set_linkspeed_policy(self, ls_policy):
+ return self.handle.fabric_config_setlinkspeedpolicy(ls_policy=ls_policy)
+
+ def fabric_config_get_uplink(self, iface=0):
+ return self.handle.fabric_config_getuplink(iface=iface)
+
+ def fabric_config_set_uplink(self, uplink=0, iface=0):
+ return self.handle.fabric_config_setuplink(uplink=uplink, iface=iface)
+
+ def fabric_config_get_link_users_factor(self):
+ return self.handle.fabric_config_getlinkusersfactor()
+
+ def fabric_config_set_link_users_factor(self, lu_factor):
+ return self.handle.fabric_config_setlinkusersfactor(lu_factor=lu_factor)
+
+ def set_bootdev(self, device, options=None):
+ return self.handle.bootdev_set(device=device, options=options)
+
+ def get_bootdev(self):
+ return self.handle.bootdev_get()
+
+ def get_bootparam(self, param):
+ return self.handle.bootparam_get(param=param)
+
+ def mc_reset(self, mode):
+ return self.handle.mc_reset(mode=mode)
+
+ def mem_read(self, length, address, fmt=None):
+ return self.handle.data_memread(length=length, addr=address,
+ fmt=fmt)
+
+ def mem_write(self, length, address, value):
+ return self.handle.data_memwrite(length=length, addr=address,
+ value=value)
+
+ def cdb_read(self, length, cid, fmt=None):
+ return self.handle.data_cdbread(length=length, cid=cid,
+ fmt=fmt)
+
+ def cdb_write(self, length, cid, value):
+ return self.handle.data_cdbwrite(length=length, cid=cid,
+ value=value)
+
+ def get_info_basic(self):
+ return self.handle.info_basic()
+
+ def get_info_card(self):
+ return self.handle.info_card()
+
+ #
+ # fabric info commands
+ #
+
+ def fabric_info_get_routing_table(self, filename, tftp_addr=None):
+ return self.handle.fabric_info_getroutingtable(filename=filename,
+ tftp_addr=tftp_addr)
+
+ def fabric_info_get_link_map(self, filename, tftp_addr=None):
+ return self.handle.fabric_info_getlinkmap(filename=filename,
+ tftp_addr=tftp_addr)
+
+ def fabric_info_get_depth_chart(self, filename, tftp_addr=None):
+ return self.handle.fabric_info_getdepthchart(filename=filename,
+ tftp_addr=tftp_addr)
+
+class LanBMC(BMC):
+ """A BMC that's accessed over the LAN"""
+ def __init__(self,
+ hostname,
+ username=None,
+ password=None,
+ authtype=None,
+ level=None,
+ port=623,
+ interface='lan',
+ **kwargs):
+
+ self.params = {
+ 'hostname' : hostname,
+ 'username' : username,
+ 'password' : password,
+ 'authtype' : authtype,
+ 'level' : level,
+ 'port' : port,
+ 'interface' : interface
+ }
+
+ super(LanBMC, self).__init__(**kwargs)
diff --git a/pyipmi/bootdev.py b/pyipmi/bootdev.py
new file mode 100644
index 0000000..7ae2d1a
--- /dev/null
+++ b/pyipmi/bootdev.py
@@ -0,0 +1,40 @@
+# 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.
+
+
+"""BootDev Result"""
+
+class BootDevSetResult(object):
+ """Object to hold bootdev set result"""
+ pass
+
+class BootDevGetResult(object):
+ """Object to hold bootdev get result"""
+ pass
diff --git a/pyipmi/bootparam.py b/pyipmi/bootparam.py
new file mode 100644
index 0000000..c4425e0
--- /dev/null
+++ b/pyipmi/bootparam.py
@@ -0,0 +1,35 @@
+# 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.
+
+
+"""BootParam Result"""
+class BootParamGetResult(object):
+ """Object to hold bootparam get result"""
+ pass
diff --git a/pyipmi/channel.py b/pyipmi/channel.py
new file mode 100644
index 0000000..6d69010
--- /dev/null
+++ b/pyipmi/channel.py
@@ -0,0 +1,51 @@
+# 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.
+
+
+"""ipmitool channel Results"""
+
+class ChannelInfoResult(object):
+ """Object to hold channel info results"""
+ pass
+
+class ChannelGetAccessResult(object):
+ """Object to hold channel get access results"""
+ pass
+
+class ChannelSetAccessResult(object):
+ """Object to hold channel set access results"""
+ pass
+
+class ChannelGetCiphersResult(object):
+ """Object to hold get channel cipher suites results"""
+ iana = None
+ auth_alg = None
+ integrity_alg = None
+ confidentiality_alg = None
diff --git a/pyipmi/chassis.py b/pyipmi/chassis.py
new file mode 100644
index 0000000..18bb8d3
--- /dev/null
+++ b/pyipmi/chassis.py
@@ -0,0 +1,118 @@
+# 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.
+
+
+"""Generic stuff about IPMI chassis
+
+A chassis in IPMI isn't necessarily the same as a physical chassis. Logically,
+it's closer to a server. For example, the chassis's power status refers to
+whether or not the server's application processor has power supplied to it
+so it can run, and the hard reset command resets the application processor,
+not the entire chassis.
+"""
+
+import time
+
+class ChassisStatus:
+ """The response to a ChassisStatus command
+
+ TODO: This is currently an odd mix of types.. there must be a better way
+ """
+
+ def __init__(self):
+ self.power_restore_policy = None
+ self.power_control_fault = None
+ self.power_fault = None
+ self.power_interlock = None
+ self.power_overload = None
+ self.power_on = None
+ self.last_power_event = None
+ self.misc_chassis_state = None
+
+class Chassis:
+ """Represents a single chassis.
+
+ This gives convenience methods for issuing chassis related commands.
+ """
+
+ def wait_after(func):
+ """Decorator for delaying after a command is issue
+
+ A lot of the chassis related commands need a while to take effect - the
+ @wait_after decorator is provided here as a way to provide a common way
+ to delay after executing a chassis command.
+
+ The chassis's wait attribute defines how long the delay after issuing
+ a command is."""
+
+ def sleeper(self):
+ """sleeps after calling func"""
+ ret = func(self)
+ time.sleep(self.wait)
+ return ret
+
+ return sleeper
+
+ def __init__(self, handle):
+ """
+ Arguments:
+ handle -- a Handle object to use to talk to a chassis
+ """
+ self._handle = handle
+ self.wait = 10
+
+ def status(self):
+ """Get the chassis's status"""
+ return self._handle.chassis_status()
+
+ @wait_after
+ def power_off(self):
+ """Turn the chassis off"""
+ self._handle.chassis_control(mode="off")
+
+ @wait_after
+ def power_on(self):
+ """Turn the chassis on"""
+ self._handle.chassis_control(mode="on")
+
+ @wait_after
+ def power_cycle(self):
+ """Power cycle the chassis"""
+ self._handle.chassis_control(mode="cycle")
+
+ @wait_after
+ def hard_reset(self):
+ """Reset the chassis"""
+ self._handle.chassis_control(mode="reset")
+
+ @property
+ def is_powered(self):
+ """True if the chassis is powered"""
+ return self._handle.chassis_status().power_on
diff --git a/pyipmi/commands/__init__.py b/pyipmi/commands/__init__.py
new file mode 100644
index 0000000..380aaf6
--- /dev/null
+++ b/pyipmi/commands/__init__.py
@@ -0,0 +1,89 @@
+# 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.
+
+
+"""IPMI commands that are implemented
+
+These don't always map directly to IPMI requests, although sometimes
+they do. Sometimes, they map to higher level commands provided by
+ipmitool. It's more convenient (and closer to real world use) to use
+these higher level commands than to break stuff down.
+"""
+from chassis import chassis_commands
+from bmc import bmc_commands
+from sdr import sdr_commands
+from fw import fw_commands
+from sel import sel_commands
+from sol import sol_commands
+from payload import payload_commands
+from dcmi import dcmi_commands
+from pef import pef_commands
+from freeipmi_pef import freeipmi_pef_commands
+from pet import pet_commands
+from event import event_commands
+from watchdog import watchdog_commands
+from fru import fru_commands
+from lan import lan_commands
+from channel import channel_commands
+from user import user_commands
+from fabric import fabric_commands
+from fabric_config import fabric_config_commands
+from bootdev import bootdev_commands
+from bootparam import bootparam_commands
+from mc import mc_commands
+from data import data_commands
+from info import info_commands
+
+ipmi_commands = {}
+
+ipmi_commands.update(bmc_commands)
+ipmi_commands.update(chassis_commands)
+ipmi_commands.update(sdr_commands)
+ipmi_commands.update(fw_commands)
+ipmi_commands.update(sel_commands)
+ipmi_commands.update(sol_commands)
+ipmi_commands.update(payload_commands)
+ipmi_commands.update(dcmi_commands)
+ipmi_commands.update(pef_commands)
+ipmi_commands.update(freeipmi_pef_commands)
+ipmi_commands.update(pet_commands)
+ipmi_commands.update(event_commands)
+ipmi_commands.update(watchdog_commands)
+ipmi_commands.update(fru_commands)
+ipmi_commands.update(lan_commands)
+ipmi_commands.update(channel_commands)
+ipmi_commands.update(user_commands)
+ipmi_commands.update(fabric_commands)
+ipmi_commands.update(fabric_config_commands)
+ipmi_commands.update(bootdev_commands)
+ipmi_commands.update(bootparam_commands)
+ipmi_commands.update(mc_commands)
+ipmi_commands.update(data_commands)
+ipmi_commands.update(info_commands)
diff --git a/pyipmi/commands/bmc.py b/pyipmi/commands/bmc.py
new file mode 100644
index 0000000..3a91569
--- /dev/null
+++ b/pyipmi/commands/bmc.py
@@ -0,0 +1,152 @@
+# 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.
+
+
+"""BMC related commands"""
+
+from .. import Command
+from .. bmc import BMCInfo, BMCGuid, BMCEnables, BMCResult, BMCResetResult
+from pyipmi.tools.responseparser import ResponseParserMixIn, str2bool
+
+class GetDeviceIdCommand(Command, ResponseParserMixIn):
+ """Describes the get_device_id IPMI command
+
+ This is "bmc info" to ipmitool
+ """
+ name = "Get Device ID"
+ result_type = BMCInfo
+
+ response_fields = {
+ "Device ID" : {},
+ "Device Revision" : {},
+ "Firmware Revision" : {},
+ "IPMI Version" : {},
+ "Manufacturer ID" : {},
+ "Product ID" : { "parser" : lambda s: s.split(' ')[0] },
+ "Device Available" : { "parser" : str2bool }
+ }
+
+ ipmitool_args = ["bmc", "info"]
+
+
+class BMCSelfTestCommand(Command, ResponseParserMixIn):
+ """Describes the get self test results IPMI command
+
+ This is "bmc selftest" to ipmitool
+ """
+ name = "BMC Self Test"
+ result_type = BMCResult
+
+ response_fields = {
+ "Selftest" : { }
+ }
+
+ ipmitool_args = ["bmc", "selftest"]
+
+
+class GetSystemGuidCommand(Command, ResponseParserMixIn):
+ """Describes the get_system_guid IPMI command
+
+ This is "bmc guid" to ipmitool
+ """
+ name = "Get System GUID"
+ result_type = BMCGuid
+
+ response_fields = {
+ "System GUID" : {}
+ }
+
+ ipmitool_args = ["bmc", "guid"]
+
+class GetCommandEnables(Command, ResponseParserMixIn):
+ """The Get Command Enables command
+
+ In ipmitool world, this is "bmc getenables"
+ """
+ name = 'Get Command Enables'
+ result_type = BMCEnables
+
+ response_fields = {
+ 'Receive Message Queue Interrupt' : {
+ 'attr' : 'recv_msg_intr',
+ 'parser' : str2bool
+ },
+ 'Event Message Buffer Full Interrupt' : {
+ 'attr' : 'event_msg_intr',
+ 'parser' : str2bool
+ },
+ 'Event Message Buffer' : {
+ 'attr' : 'event_msg',
+ 'parser' : str2bool
+ },
+ 'System Event Logging' : {
+ 'attr' : 'system_event_log',
+ 'parser' : str2bool
+ },
+ 'OEM 0' : {
+ 'attr' : 'oem0',
+ 'parser' : str2bool
+ },
+ 'OEM 1' : {
+ 'attr' : 'oem1',
+ 'parser' : str2bool
+ },
+ 'OEM 2' : {
+ 'attr' : 'oem2',
+ 'parser' : str2bool
+ }
+ }
+
+ ipmitool_args = ['bmc', 'getenables']
+
+
+class BMCResetCommand(Command, ResponseParserMixIn):
+ """The Get Command Enables command
+
+ In ipmitool world, this is "bmc reset [warm|cold]"
+ """
+ name = 'BMC Reset'
+ result_type = BMCResetResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ['bmc', 'reset', self._params['type']]
+
+
+bmc_commands = {
+ 'get_device_id' : GetDeviceIdCommand,
+ 'get_system_guid' : GetSystemGuidCommand,
+ 'get_command_enables' : GetCommandEnables,
+ 'selftest' : BMCSelfTestCommand,
+ 'bmc_reset' : BMCResetCommand
+}
diff --git a/pyipmi/commands/bootdev.py b/pyipmi/commands/bootdev.py
new file mode 100644
index 0000000..3be6b45
--- /dev/null
+++ b/pyipmi/commands/bootdev.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 .. import Command
+from pyipmi.tools.responseparser import ResponseParserMixIn
+from pyipmi.bootdev import *
+from pyipmi.commands.bootparam import *
+
+class BootDevSetCommand(Command, ResponseParserMixIn):
+ """ Set Boot Device using ipmitool chassis bootdev """
+
+ name = "Set Boot Device"
+ result_type = BootDevSetResult
+
+ response_fields = {
+ 'Boot Device' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ if self._params['options'] != None:
+ options = "options=" + str(self._params['options'])
+ return ["chassis", "bootdev", self._params['device'],options]
+ return ["chassis", "bootdev", self._params['device']]
+
+class BootDevGetCommand(Command, ResponseParserMixIn):
+ """ Get Boot Device using ipmitool chassis bootdev """
+
+ name = "Get Boot Device"
+ result_type = BootDevGetResult
+ """ parse the output into a nice harsh"""
+
+ def parse_response(self, out, err):
+ """ Use bootParam parse method to do the job of parsing output into a nice harsh """
+ boot_param = BootParamGetCommand(self._tool)
+ all_info = boot_param.parse_response(out=out,err=err)
+ bool2str = {True:'Yes',False:'No'}
+
+ """ Only interested in Device and Persistent (Y/N)"""
+ device = all_info['Boot Device Selector']
+ persistent = (all_info['Options apply'] == 'all future boots')
+ return {'device':device, 'persistent':bool2str[persistent]}
+
+ """Get 5 from bootparam provides the status of bootdev"""
+ @property
+ def ipmitool_args(self):
+ return ["chassis", "bootparam", "get", "5"]
+
+bootdev_commands = {
+ "bootdev_get" : BootDevGetCommand,
+ "bootdev_set" : BootDevSetCommand
+}
diff --git a/pyipmi/commands/bootparam.py b/pyipmi/commands/bootparam.py
new file mode 100644
index 0000000..08612c6
--- /dev/null
+++ b/pyipmi/commands/bootparam.py
@@ -0,0 +1,66 @@
+# 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 .. import Command
+from pyipmi.tools.responseparser import ResponseParserMixIn
+from pyipmi.bootparam import *
+
+class BootParamGetCommand(Command, ResponseParserMixIn):
+ """ Get Boot Parameters using ipmitool chassis bootparam # """
+
+ name = "Get Boot Device"
+ result_type = BootParamGetResult
+
+ def parse_response(self, out, err):
+ """ Output is a number of lines with some info
+ """
+ result = {}
+ delimiter = [':','is','to']
+ for line in out.strip().split('\n'):
+ key, value = line, ""
+ for x in delimiter:
+ if x in line:
+ info = line.split(x)
+ key, value = info[0], info[1]
+ break
+ key = key.strip(' -')
+ value = value.strip(' -')
+ result[key] = value
+ return result
+
+ """ get #param from bootparam """
+ @property
+ def ipmitool_args(self):
+ return ["chassis", "bootparam", "get",self._params['param']]
+
+bootparam_commands = {
+ "bootparam_get" : BootParamGetCommand
+}
diff --git a/pyipmi/commands/channel.py b/pyipmi/commands/channel.py
new file mode 100644
index 0000000..200e193
--- /dev/null
+++ b/pyipmi/commands/channel.py
@@ -0,0 +1,201 @@
+# 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.
+
+
+"""channel related commands"""
+
+from .. import Command
+from pyipmi.channel import *
+from pyipmi.tools.responseparser import ResponseParserMixIn
+import re
+
+
+class ChannelInfoCommand(Command, ResponseParserMixIn):
+ """Describes the get channel info IPMI command
+
+ This is "channel info" to ipmitool
+ """
+
+ def parse_response(self, out, err):
+ """ Strip out extraneous colons to allow more generic parsing
+ """
+ out_list = map(lambda x: x.strip(), out.split('\n'))
+ new_out_list = []
+
+ setting_prefix = 'Active'
+ for line in out_list:
+ m = re.match("Channel 0x([0-9a-fA-F]+) info:", line)
+ if m:
+ line = "Channel : %s" % m.group(1)
+
+ m = re.match("(Alerting|Per-message Auth|User Level Auth|Access Mode)\s+:\s+(\S+)", line)
+ if m:
+ line = "%s %s : %s" % (setting_prefix, m.group(1), m.group(2))
+
+ m = re.match("Volatile\(active\) Settings", line)
+ if m:
+ setting_prefix = 'Active'
+ continue
+
+ m = re.match("Non-Volatile Settings", line)
+ if m:
+ setting_prefix = 'NV'
+ continue
+
+ new_out_list.append(line)
+
+ new_out = '\n'.join(new_out_list)
+ return self.response_parser(new_out, err)
+
+ name = "Channel Info"
+ result_type = ChannelInfoResult
+
+ response_fields = {
+ 'Channel' : {},
+ 'Channel Medium Type' : {},
+ 'Channel Protocol Type' : {},
+ 'Session Support' : {},
+ 'Active Session Count' : {},
+ 'Protocol Vendor ID' : {},
+ 'Active Alerting' : {},
+ 'Active Per-message Auth' : {},
+ 'Active User Level Auth' : {},
+ 'Active Access Mode' : {},
+ 'NV Alerting' : {},
+ 'NV Per-message Auth' : {},
+ 'NV User Level Auth' : {},
+ 'NV Access Mode' : {}
+ }
+
+ ipmitool_args = ["channel", "info"]
+
+
+class ChannelGetAccessCommand(Command, ResponseParserMixIn):
+ """Describes the get channel access IPMI command
+
+ This is "channel getaccess" to ipmitool
+ """
+
+ response_parser = ResponseParserMixIn.parse_colon_record_list
+
+ name = "Channel Get Access"
+ result_type = ChannelGetAccessResult
+
+ response_fields = {
+ 'Maximum User IDs' : {},
+ 'Enabled User IDs' : {},
+ 'User ID' : {},
+ 'User Name' : {},
+ 'Fixed Name' : {},
+ 'Access Available' : {},
+ 'Link Authentication' : {},
+ 'IPMI Messaging' : {},
+ 'Privilege Level' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["channel", "getaccess", self._params['channel'],
+ self._params['userid']]
+
+
+class ChannelSetAccessCommand(Command, ResponseParserMixIn):
+ """Describes the set channel access IPMI command
+
+ This is "channel setaccess" to ipmitool
+ """
+
+ name = "Channel Set Access"
+ result_type = ChannelSetAccessResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ callin = ipmi = link = priv_level = ""
+
+ if self._params.get('callin'):
+ callin = "callin=%s" % self._params.get('callin')
+ if self._params.get('ipmi'):
+ ipmi = "ipmi=%s" % self._params.get('ipmi')
+ if self._params.get('link'):
+ link = "link=%s" % self._params.get('link')
+ if self._params.get('priv_level'):
+ priv_level = "privilege=%s" % self._params.get('priv_level')
+
+ return ["channel", "setaccess", self._params['channel'],
+ self._params['userid'], callin, ipmi, link, priv_level]
+
+
+class ChannelGetCiphersCommand(Command, ResponseParserMixIn):
+ """Describes the get channel cipher suites IPMI command
+
+ This is "channel getciphers <ipmi | sol>" to ipmitool
+ """
+
+ def parse_response(self, out, err):
+ """ Strip out extraneous colons to allow more generic parsing
+ """
+ out.strip()
+ output_list = map(lambda x: x.strip(), out.split('\n'))
+ result = {}
+
+ for line in output_list:
+ if line == '':
+ continue
+ line_list = map(lambda x: x.strip(), line.split())
+ if line_list[0] == 'ID':
+ continue
+ suite = line_list[0]
+ result[suite] = ChannelGetCiphersResult()
+ result[suite].iana = line_list[1]
+ result[suite].auth_alg = line_list[2]
+ result[suite].integrity_alg = line_list[3]
+ result[suite].confidentiality_alg = line_list[4]
+
+ return result
+
+ name = "Channel Get Cipher Suites"
+ result_type = ChannelGetCiphersResult
+
+ @property
+ def ipmitool_args(self):
+ return ["channel", "getciphers", self._params['mode']]
+
+
+channel_commands = {
+ 'channel_info' : ChannelInfoCommand,
+ 'channel_get_access' : ChannelGetAccessCommand,
+ 'channel_set_access' : ChannelSetAccessCommand,
+ 'channel_get_ciphers' : ChannelGetCiphersCommand
+}
diff --git a/pyipmi/commands/chassis.py b/pyipmi/commands/chassis.py
new file mode 100644
index 0000000..1845902
--- /dev/null
+++ b/pyipmi/commands/chassis.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.
+
+
+"""Chassis related IPMI commands"""
+from .. import Command
+from .. chassis import ChassisStatus
+from pyipmi.tools.responseparser import ResponseParserMixIn, str2bool
+
+class ChassisStatusCommand(Command, ResponseParserMixIn):
+ """Describes the chassis status IPMI command"""
+
+ name = 'Chassis Status'
+
+ ipmitool_args = ['chassis', 'status']
+ result_type = ChassisStatus
+
+ response_fields = {
+ 'System Power' : {'attr' : 'power_on', 'parser' : lambda v: v == 'on'},
+ 'Power Overload' : {'parser' : str2bool},
+ 'Power Interlock' : {},
+ 'Main Power Fault' : {'parser' : str2bool},
+ 'Power Control Fault' : {'parser' : str2bool},
+ 'Power Restore Policy' : {}
+ }
+
+class ChassisControlCommand(Command, ResponseParserMixIn):
+ """Describes the IPMI chassis control command
+
+ ipmitool calls this "chassis power"
+ """
+
+ @property
+ def ipmitool_args(self):
+ """The chassis control command takes a 'mode' parameter
+
+ Look at ipmitool's manpage for more info.
+ """
+ return ['chassis', 'power', self._params['mode']]
+
+class ChassisPolicyCommand(Command, ResponseParserMixIn):
+ """Describes the IPMI chassis policy command"""
+
+ @property
+ def ipmitool_args(self):
+ """The chassis policy command takes a 'state' parameter"""
+ return ['chassis', 'policy', self._params['state']]
+
+chassis_commands = {
+ 'chassis_status' : ChassisStatusCommand,
+ 'chassis_control' : ChassisControlCommand,
+ 'chassis_policy' : ChassisPolicyCommand
+}
diff --git a/pyipmi/commands/data.py b/pyipmi/commands/data.py
new file mode 100644
index 0000000..5fe5b1b
--- /dev/null
+++ b/pyipmi/commands/data.py
@@ -0,0 +1,125 @@
+# 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 .. import Command
+from pyipmi.tools.responseparser import ResponseParserMixIn
+from pyipmi.data import *
+
+class DataMemReadCommand(Command, ResponseParserMixIn):
+ """ Describes the cxoem data mem read command
+ """
+
+ name = "Read value from memory"
+ result_type = DataMemReadResult
+
+ response_fields = {
+ 'Length' : {},
+ 'Addr' : {},
+ 'Value' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ args = ["cxoem", "data", "mem", "read",
+ self._params['length'], self._params['addr']]
+ if self._params['fmt']:
+ args.append(self._params['fmt'])
+ return args
+
+
+class DataMemWriteCommand(Command, ResponseParserMixIn):
+ """ Describes the cxoem data mem write command
+ """
+
+ name = "Write value to memory"
+ result_type = DataMemWriteResult
+
+ response_fields = {
+ 'Length' : {},
+ 'Addr' : {},
+ 'Value' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["cxoem", "data", "mem", "write", self._params['length'],
+ self._params['addr'], self._params['value']]
+
+
+class DataCDBReadCommand(Command, ResponseParserMixIn):
+ """ Describes the cxoem data cdb read command
+ """
+
+ name = "Read value from CDB"
+ result_type = DataCDBReadResult
+
+ response_fields = {
+ 'Length' : {},
+ 'Cid' : {},
+ 'Data size' : {},
+ 'CID size' : {},
+ 'Value' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ args = ["cxoem", "data", "cdb", "read",
+ self._params['length'], self._params['cid']]
+ if self._params['fmt']:
+ args.append(self._params['fmt'])
+ return args
+
+
+class DataCDBWriteCommand(Command, ResponseParserMixIn):
+ """ Describes the cxoem data cdb write command
+ """
+
+ name = "Write value to CDB"
+ result_type = DataCDBWriteResult
+
+ response_fields = {
+ 'Length' : {},
+ 'Cid' : {},
+ 'Value' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["cxoem", "data", "cdb", "write", self._params['length'],
+ self._params['cid'], self._params['value']]
+
+
+data_commands = {
+ "data_memread" : DataMemReadCommand,
+ "data_memwrite" : DataMemWriteCommand,
+ "data_cdbread" : DataCDBReadCommand,
+ "data_cdbwrite" : DataCDBWriteCommand
+}
diff --git a/pyipmi/commands/dcmi.py b/pyipmi/commands/dcmi.py
new file mode 100644
index 0000000..31c8a8f
--- /dev/null
+++ b/pyipmi/commands/dcmi.py
@@ -0,0 +1,337 @@
+# 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 .. import Command
+from pyipmi.tools.responseparser import *
+from pyipmi.dcmi import *
+
+class DCMICommandWithErrors(Command, ResponseParserMixIn):
+
+ # TODO: Generalize this to base class? Better way to include
+ # error output in parsing?
+ def parse_response(self, out, err):
+ """Parse the response to a command
+
+ The 'response_format' attribute is used to determine
+ what parser to use to for interpreting the results.
+
+ Arguments:
+ out -- the text response of a command from stdout
+ err -- the text response of a command from stderr
+ """
+
+ out = out + err
+ return self.response_parser(out, err)
+
+class DCMIGetCapabilitiesCommand(DCMICommandWithErrors):
+ """Describes the DCMI get capabilities command
+
+ """
+
+ name = "Get DCMI Capabilities"
+ result_type = DCMIGetCapabilitiesResult
+
+ response_fields = {
+ 'DCMI Specification Conformance' : {},
+ 'Identification Support' : {},
+ 'SEL logging' : {},
+ 'Chassis Power' : {},
+ 'Temperature Monitor' : {},
+ 'Power Management / Monitoring Support' : {},
+ 'In-band System Interface Channel' : {},
+ 'Serial TMODE' : {},
+ 'Out-Of-Band Secondary LAN Channel' : {},
+ 'Out-Of-Band Primary LAN Channel' : {},
+ 'SOL' : {},
+ 'VLAN' : {},
+ 'Number of SEL entries' : {},
+ 'SEL automatic rollover' : {},
+ 'GUID' : {},
+ 'DHCP Host Name' : {},
+ 'Asset Tag' : {},
+ 'Inlet temperature' : {},
+ 'Processors temperature' : {},
+ 'Baseboard temperature' : {},
+ 'Power Management Device Slave Address' : {},
+ 'Power Management Controller Device Revision' : {},
+ 'Power Management Controller Channel Number' : {},
+ 'Primary LAN Out-of-band Channel Number' : {},
+ 'Secondary LAN Out-of-band Channel Number' : {},
+ 'Serial Out-of-band TMODE Capability Channel Number' : {}
+ }
+
+ ipmidcmi_args = ["--get-dcmi-capability-info"]
+
+class DCMISetAssetTagCommand(DCMICommandWithErrors):
+ """Describes the DCMI set asset tag command
+
+ """
+
+ name = "Set DCMI Asset Tag"
+ result_type = DCMISetAssetTagResult
+
+ # No response -- Have to do a get to confirm
+ response_fields = {
+ }
+
+ @property
+ def ipmidcmi_args(self):
+ """
+ """
+ return ["--set-asset-tag", self._params['tag']]
+
+class DCMIGetAssetTagCommand(DCMICommandWithErrors):
+ """Describes the dcmi get asset tag command
+
+ """
+
+ response_parser = ResponseParserMixIn.parse_single_line
+
+ name = "Get DCMI Asset Tag"
+ result_type = DCMIGetAssetTagResult
+
+ response_fields = {
+ 'attr' : 'tag'
+ }
+
+ ipmidcmi_args = ["--get-asset-tag"]
+
+
+class DCMIGetManagementControllerID(DCMICommandWithErrors):
+ """Describes the DCMI get management controller ID string command
+
+ """
+
+ response_parser = ResponseParserMixIn.parse_single_line
+
+ name = "Get Management Controller ID String"
+ result_type = DCMIGetManagementControllerIDResult
+
+ response_fields = {
+ 'attr' : 'DCMI'
+ }
+
+ ipmidcmi_args = ["--get-management-controller-identifier-string"]
+
+class DCMISetManagementControllerID(DCMICommandWithErrors):
+ """Describes the DCMI get management controller ID string command
+
+ """
+ response_parser = ResponseParserMixIn.parse_single_line
+
+ name = "Set Management Controller ID String"
+ result_type = DCMISetManagementControllerIDResult
+
+ response_fields = {
+ 'attr' : 'DCMI'
+ }
+
+ @property
+ def ipmidcmi_args(self):
+ """
+ """
+ return ["--set-management-controller-identifier-string", self._params['controller']]
+class DCMIGetSensorInfo(DCMICommandWithErrors):
+ """Describes the DCMI get sensor info command
+
+ """
+ def parse_response(self, out, err):
+ """ Output is a number of lines with some info
+ """
+ new_out_list = []
+ expected_fields = ['Inlet Temperature','CPU Temperature','Baseboard temperature']
+ for line in out.strip().split('\n'):
+ for field in expected_fields:
+ if field in line:
+ value = line.lstrip(field)
+ new_line = field + " : " + value
+ new_out_list.append(new_line)
+ new_output= "\n".join(new_out_list)
+ return self.response_parser(new_output, err)
+
+ name = "Get DCMI Sensor Info"
+ result_type = DCMIGetSensorInfoResult
+ response_fields = {
+ 'Inlet Temperature':{},
+ 'CPU Temperature' : {},
+ 'Baseboard temperature' :{}
+ }
+
+
+ ipmidcmi_args = ["--get-dcmi-sensor-info"]
+
+
+class DCMIGetPowerStatistics(DCMICommandWithErrors):
+ """Describes the DCMI get system power statistics command
+
+ """
+
+ name = "Get Power Statistics"
+ result_type = DCMIGetPowerStatisticsResult
+
+ response_fields = {
+ 'Current Power' : {},
+ 'Minimum Power over sampling duration' : {},
+ 'Maximum Power over sampling duration' : {},
+ 'Average Power over sampling duration' : {},
+ 'Time Stamp' : {},
+ 'Statistics reporting time period' : {},
+ 'Power Measurement' : {}
+ }
+
+ ipmidcmi_args = ["--get-system-power-statistics"]
+
+
+class DCMIGetPowerLimit(DCMICommandWithErrors):
+ """Describes the DCMI get power limit command
+
+ """
+ name = "Get Power Limit"
+ result_type = DCMIGetPowerLimitResult
+
+ response_fields = {
+ 'Exception Actions' : {} ,
+ 'Power Limit Requested' : {},
+ 'Correction time limit' : {},
+ 'Management application Statistics Sampling period' :{}
+ }
+
+ ipmidcmi_args = ["--get-power-limit"]
+
+
+class DCMISetPowerLimit(DCMICommandWithErrors):
+ """Describes the DCMI set power limit command
+
+ """
+
+ name = "Set Power Limit"
+ result_type = DCMISetPowerLimitResult
+
+ response_fields = {
+ }
+
+ ipmidcmi_args = ["--set-power-limit"]
+
+class DCMIPowerLimitRequested(DCMICommandWithErrors):
+ """Describes the DCMI power limit requested command
+
+ """
+
+ name = "Power Limit Requested (Watts)"
+ result_type = DCMIPowerLimitRequestedResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmidcmi_args(self):
+ """ """
+ if self._params['exception'] is None:
+ return ["--set-power-limit","--power-limit-requested", self._params['limit']]
+ else:
+ return ["--set-power-limit","--power-limit-requested", self._params['limit'],
+ "--exception-actions", self._params['exception']]
+
+class DCMICorrectionTimeLimit(DCMICommandWithErrors):
+ """Describes the DCMI correction time limit command
+
+ """
+
+ name = "Power Correction Time Limit (Milliseconds)"
+ result_type = DCMICorrectionTimeLimitResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmidcmi_args(self):
+ """
+ """
+ if self._params['exception'] is None:
+ return ["--set-power-limit","--correction-time-limit", self._params['time_limit']]
+ else:
+ return ["--set-power-limit","--correction-time-limit", self._params['time_limit'],
+ "--exception-actions", self._params['exception']]
+
+class DCMIStatisticsSamplingPeriod(DCMICommandWithErrors):
+ """Describes the DCMI statistics sampling period command
+
+ """
+ name = "Power Statistics Sampling Period ( seconds)"
+ result_type = DCMIStatisticsSamplingPeriodResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmidcmi_args(self):
+ """ """
+ if self._params['exception'] is None:
+ return ["--set-power-limit","--statistics-sampling-period", self._params['period']]
+ else:
+ return ["--set-power-limit","--statistics-sampling-period", self._params['period'],
+ "--exception-actions", self._params['exception']]
+
+
+class DCMIActivatePowerLimit(DCMICommandWithErrors):
+ """Describes the DCMI activate/deactivate power limit command
+
+ """
+
+ response_parser = ResponseParserMixIn.parse_single_line
+
+ name = "Activate Or Deactivate Power Limit"
+ result_type = DCMIActivatePowerLimitResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmidcmi_args(self):
+ """
+ """
+ return ["--activate-deactivate-power-limit", self._params['action']]
+
+dcmi_commands = {
+ "dcmi_get_capabilities" : DCMIGetCapabilitiesCommand,
+ "dcmi_set_asset_tag" : DCMISetAssetTagCommand,
+ "dcmi_get_asset_tag" : DCMIGetAssetTagCommand,
+ "dcmi_get_controller_id" : DCMIGetManagementControllerID,
+ "dcmi_set_controller_id" : DCMISetManagementControllerID,
+ "dcmi_get_sensor_info" : DCMIGetSensorInfo,
+ "dcmi_get_power_statistics" : DCMIGetPowerStatistics,
+ "dcmi_get_power_limit" : DCMIGetPowerLimit,
+ "dcmi_set_power_limit" : DCMISetPowerLimit,
+ "dcmi_power_limit_requested": DCMIPowerLimitRequested,
+ "dcmi_activate_power_limit" : DCMIActivatePowerLimit,
+ "dcmi_correction_time_limit" : DCMICorrectionTimeLimit,
+ "dcmi_statistics_sampling_period": DCMIStatisticsSamplingPeriod
+}
diff --git a/pyipmi/commands/event.py b/pyipmi/commands/event.py
new file mode 100644
index 0000000..2aca6dc
--- /dev/null
+++ b/pyipmi/commands/event.py
@@ -0,0 +1,79 @@
+# 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.
+
+
+"""event related commands -- for generating test events"""
+
+from .. import Command
+from pyipmi.event import *
+from pyipmi.tools.responseparser import ResponseParserMixIn
+
+
+class GenerateGenericEvent(Command, ResponseParserMixIn):
+ """Describes the generic event IPMI command
+
+ This is "event <event_type>" to ipmitool
+ """
+ name = "Generate Generic Event"
+ result_type = GenericEventResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["event", self._params['event_type']]
+
+
+class AssertSensorEvent(Command, ResponseParserMixIn):
+ """Describes the generic event IPMI command
+
+ This is "event <sensorid> <state> assert" to ipmitool
+ """
+ name = "Assert Sensor Event"
+ result_type = AssertSensorEventResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["event", self._params['sensor_id'],
+ self._params['state'], 'assert']
+
+
+event_commands = {
+ 'generic_event' : GenerateGenericEvent,
+ 'assert_sensor_event' : AssertSensorEvent
+}
diff --git a/pyipmi/commands/fabric.py b/pyipmi/commands/fabric.py
new file mode 100644
index 0000000..c28a6fb
--- /dev/null
+++ b/pyipmi/commands/fabric.py
@@ -0,0 +1,306 @@
+# 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.
+
+"""
+This module holdes cxoem "fabric" commands other than "fabric config"
+commands.
+"""
+
+from .. import Command
+from pyipmi.tools.responseparser import ResponseParserMixIn
+from pyipmi.fabric import *
+from pyipmi import IpmiError
+
+class CommandWithErrors(Command, ResponseParserMixIn):
+
+ def parse_response(self, out, err):
+ """Parse the response to a command
+
+ The 'ipmitool_response_format' attribute is used to determine
+ what parser to use to for interpreting the results.
+
+ Arguments:
+ out -- the text response of an command from stdout
+ err -- the text response of an command from stderr
+ """
+
+ out = out + err
+ return self.response_parser(out, err)
+
+class UpdateConfigCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric update config command"""
+ name = "Update Config"
+ result_type = FabricUpdateConfigResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["cxoem", "fabric", "update_config"]
+
+class GetNodeIDCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric get nodeid command"""
+ name = "Get NodeID command"
+ result_type = int
+
+ def parse_response(self, out, err):
+ if err:
+ raise IpmiError(err)
+ return int(out)
+
+ response_fields = {
+ }
+
+ ipmitool_args = ["cxoem", "fabric", "get", "nodeid"]
+
+class GetIPAddrCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric get ipaddr command"""
+ name = "Get ipaddr command"
+ result_type = str
+
+ def parse_response(self, out, err):
+ return out.strip()
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ result = ["cxoem", "fabric", "get", "ipaddr"]
+ if self._params.get('nodeid', None):
+ result.extend(['node', self._params['nodeid']])
+ if self._params.get('iface', None):
+ result.extend(['interface', self._params['iface']])
+ return result
+
+class GetMacAddrCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric get macaddr command"""
+ name = "Get macaddr command"
+ result_type = str
+
+ def parse_response(self, out, err):
+ if err:
+ raise IpmiError(err)
+ return out.strip()
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ result = ["cxoem", "fabric", "get", "macaddr", "interface",
+ self._params['iface']]
+ if self._params.get('nodeid', None):
+ result.extend(['node', self._params['nodeid']])
+ return result
+
+class AddMacAddrCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric add macaddr command"""
+ name = "Add macaddr command"
+
+ @property
+ def ipmitool_args(self):
+ result = ['cxoem', 'fabric', 'add',
+ 'macaddr', self._params['macaddr'],
+ 'interface', self._params['iface']]
+ if self._params['nodeid']:
+ result += ['node', self._params['nodeid']]
+ return result
+
+
+class RmMacAddrCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric rm macaddr command"""
+ name = "Remove macaddr command"
+
+ @property
+ def ipmitool_args(self):
+ result = ['cxoem', 'fabric', 'rm',
+ 'macaddr', self._params['macaddr'],
+ 'interface', self._params['iface']]
+ if self._params['nodeid']:
+ result += ['node', self._params['nodeid']]
+ return result
+
+class GetLinkspeedCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric get linkspeed command"""
+ name = "Get linkspeed command"
+ result_type = float
+
+ def parse_response(self, out, err):
+ if err:
+ raise IpmiError(err)
+ return float(out)
+
+ @property
+ def ipmitool_args(self):
+ result = ['cxoem', 'fabric', 'get', 'linkspeed']
+ if self._params.get('link', None):
+ result.extend(['link', self._params['link']])
+ if self._params.get('actual', None):
+ result.extend(['actual'])
+ return result
+
+class GetLinkStatsCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric info link_stats command"""
+ name = "Get link_stats command"
+ result_type = FabricGetLinkStatsResult
+ response_fields = {
+ 'File Name' : {},
+ 'Error' : {}
+ }
+
+ def parse_response(self, out, err):
+ return out.strip()
+
+ @property
+ def ipmitool_args(self):
+ if self._params['tftp_addr'] != None:
+ tftp_args = self._params['tftp_addr'].split(":")
+ if len(tftp_args) == 1:
+ return [
+ 'cxoem', 'fabric', 'info', 'link_stats',
+ 'link', self._params['link'],
+ 'tftp', tftp_args[0],
+ 'file', self._params['filename']
+ ]
+ else:
+ return [
+ 'cxoem', 'fabric', 'info', 'link_stats',
+ 'link', self._params['link'],
+ 'tftp', tftp_args[0],
+ 'port', tftp_args[1],
+ 'file', self._params['filename']
+ ]
+ else:
+ return [
+ 'cxoem', 'fabric', 'info', 'link_stats',
+ 'link', self._params['link'],
+ 'file', self._params['filename']
+ ]
+
+class GetLinkMapCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric info linkmap command"""
+ name = "Get linkmap command"
+ result_type = FabricGetLinkMapResult
+ response_fields = {
+ 'File Name' : {},
+ 'Error' : {}
+ }
+
+ def parse_response(self, out, err):
+ return out.strip()
+
+ @property
+ def ipmitool_args(self):
+ if self._params['tftp_addr'] != None:
+ tftp_args = self._params['tftp_addr'].split(":")
+ if len(tftp_args) == 1:
+ return ["cxoem", "fabric", "info", "linkmap", "tftp",
+ tftp_args[0], "file", self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "info", "linkmap", "tftp",
+ tftp_args[0], "port", tftp_args[1], "file",
+ self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "info", "linkmap", "file",
+ self._params['filename']]
+
+class GetRoutingTableCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric info routing_table command"""
+ name = "Get routing_table command"
+ result_type = FabricGetRoutingTableResult
+ response_fields = {
+ 'File Name' : {},
+ 'Error' : {}
+ }
+
+ def parse_response(self, out, err):
+ return out.strip()
+
+ @property
+ def ipmitool_args(self):
+ if self._params['tftp_addr'] != None:
+ tftp_args = self._params['tftp_addr'].split(":")
+ if len(tftp_args) == 1:
+ return ["cxoem", "fabric", "info", "routing_table", "tftp",
+ tftp_args[0], "file", self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "info", "routing_table", "tftp",
+ tftp_args[0], "port", tftp_args[1], "file",
+ self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "info", "routing_table", "file",
+ self._params['filename']]
+
+class GetDepthChartCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric info depth_chart command"""
+ name = "Get depth_chart command"
+ result_type = FabricGetDepthChartResult
+ response_fields = {
+ 'File Name' : {},
+ 'Error' : {}
+ }
+
+ def parse_response(self, out, err):
+ return out.strip()
+
+ @property
+ def ipmitool_args(self):
+ if self._params['tftp_addr'] != None:
+ tftp_args = self._params['tftp_addr'].split(":")
+ if len(tftp_args) == 1:
+ return ["cxoem", "fabric", "info", "depth_chart", "tftp",
+ tftp_args[0], "file", self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "info", "depth_chart", "tftp",
+ tftp_args[0], "port", tftp_args[1], "file",
+ self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "info", "depth_chart", "file",
+ self._params['filename']]
+
+fabric_commands = {
+ "fabric_updateconfig" :UpdateConfigCommand,
+ "fabric_getnodeid" : GetNodeIDCommand,
+ "fabric_getipaddr" : GetIPAddrCommand,
+ "fabric_getmacaddr" : GetMacAddrCommand,
+ "fabric_getlinkspeed" : GetLinkspeedCommand,
+ "fabric_getlinkstats" : GetLinkStatsCommand,
+ "fabric_getlinkmap" : GetLinkMapCommand,
+ "fabric_getdepthchart" : GetDepthChartCommand,
+ "fabric_getroutingtable" : GetRoutingTableCommand,
+ "fabric_addmacaddr" : AddMacAddrCommand,
+ "fabric_rmmacaddr" : RmMacAddrCommand,
+
+ "fabric_info_getroutingtable" : GetRoutingTableCommand,
+ "fabric_info_getlinkmap" : GetLinkMapCommand,
+ "fabric_info_getdepthchart" : GetDepthChartCommand,
+}
diff --git a/pyipmi/commands/fabric_config.py b/pyipmi/commands/fabric_config.py
new file mode 100644
index 0000000..fe6b92f
--- /dev/null
+++ b/pyipmi/commands/fabric_config.py
@@ -0,0 +1,288 @@
+# 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 .. import Command
+from pyipmi.tools.responseparser import ResponseParserMixIn
+from pyipmi.fabric import *
+from pyipmi import IpmiError
+
+class CommandWithErrors(Command, ResponseParserMixIn):
+
+ def parse_response(self, out, err):
+ """Parse the response to a command
+
+ The 'ipmitool_response_format' attribute is used to determine
+ what parser to use to for interpreting the results.
+
+ Arguments:
+ out -- the text response of an command from stdout
+ err -- the text response of an command from stderr
+ """
+
+ out = out + err
+ return self.response_parser(out, err)
+
+class GetIPInfoCommand(CommandWithErrors):
+ """ Describes the cxoem fabric list_ip_addrs IPMI command
+ """
+
+ name = "Retrieve fabric IP info"
+ result_type = FabricGetIPInfoResult
+
+ response_fields = {
+ 'File Name' : {},
+ 'Error' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ if self._params['tftp_addr'] != None:
+ tftp_args = self._params['tftp_addr'].split(":")
+ if len(tftp_args) == 1:
+ return ["cxoem", "fabric", "config", "get", "ipinfo", "tftp",
+ tftp_args[0], "file", self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "config", "get", "ipinfo", "tftp",
+ tftp_args[0], "port", tftp_args[1], "file",
+ self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "config", "get", "ipinfo", "file",
+ self._params['filename']]
+
+class GetUplinkInfoCommand(CommandWithErrors):
+ """ Describes the cxoem fabric list_ip_addrs IPMI command
+ """
+
+ name = "Retrieve fabric Uplink info"
+ result_type = FabricGetUplinkInfoResult
+
+ response_fields = {
+ 'File Name' : {},
+ 'Error' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ if self._params['tftp_addr'] != None:
+ tftp_args = self._params['tftp_addr'].split(":")
+ if len(tftp_args) == 1:
+ return ["cxoem", "fabric", "config", "get", "uplink_info",
+ "tftp", tftp_args[0], "file", self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "config", "get", "uplink_info",
+ "tftp", tftp_args[0], "port", tftp_args[1], "file",
+ self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "config", "get", "uplink_info", "file",
+ self._params['filename']]
+
+class GetMACAddressesCommand(CommandWithErrors):
+ """ Describes the cxoem fabric list_macs IPMI command
+ """
+
+ name = "Retrieve fabric MAC addresses"
+ result_type = FabricGetMACAddressesResult
+
+ response_fields = {
+ 'File Name' : {},
+ 'Error' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ if self._params['tftp_addr'] != None:
+ tftp_args = self._params['tftp_addr'].split(":")
+ if len(tftp_args) == 1:
+ return ["cxoem", "fabric", "config", "get", "macaddrs", "tftp",
+ tftp_args[0], "file", self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "config", "get", "macaddrs", "tftp",
+ tftp_args[0], "port", tftp_args[1], "file",
+ self._params['filename']]
+ else:
+ return ["cxoem", "fabric", "config", "get", "macaddrs", "file",
+ self._params['filename']]
+
+class UpdateConfigCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric config update config command"""
+ name = "Update Config"
+
+ ipmitool_args = ['cxoem', 'fabric', 'config', 'update_config']
+
+class GetIPSrcCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric get ipsrc command"""
+ name = "Get ipsrc command"
+ result_type = int
+
+ def parse_response(self, out, err):
+ return int(out)
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["cxoem", "fabric", "config", "get", "ipsrc"]
+
+class SetIPSrcCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric set ipsrc command"""
+ name = "Set ipsrc command"
+
+ @property
+ def ipmitool_args(self):
+ return ['cxoem',
+ 'fabric',
+ 'config',
+ 'set',
+ 'ipsrc',
+ self._params['ipsrc_mode']]
+
+class FactoryDefaultCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric config factory_default command"""
+ name = "Fabric config factory_default command"
+
+ ipmitool_args = ['cxoem', 'fabric', 'config', 'factory_default']
+
+class GetIPAddrBase(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric config get ipaddr_base command"""
+ name = "Get fabric config ipaddr_base command"
+ result_type = str
+
+ ipmitool_args = ['cxoem', 'fabric', 'config', 'get', 'ipaddr_base']
+
+ def parse_response(self, out, err):
+ return out.strip()
+
+class GetLinkspeedCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric config get linkspeed command"""
+ name = "Get global linkspeed command"
+ result_type = float
+
+ def parse_response(self, out, err):
+ if err:
+ raise IpmiError(err)
+ return float(out)
+
+ ipmitool_args = ['cxoem', 'fabric', 'config', 'get', 'linkspeed']
+
+class SetLinkspeedCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric config set linkspeed command"""
+ name = "Set linkspeed command"
+
+ @property
+ def ipmitool_args(self):
+ return ['cxoem', 'fabric', 'config', 'set', 'linkspeed',
+ self._params['linkspeed']]
+
+class GetLinkspeedPolicyCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric config get ls_policy command"""
+ name = "Get global ls_policy command"
+ result_type = int
+
+ def parse_response(self, out, err):
+ if err:
+ raise IpmiError(err)
+ return int(out)
+
+ ipmitool_args = ['cxoem', 'fabric', 'config', 'get', 'ls_policy']
+
+class SetLinkspeedPolicyCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric config set ls_policy command"""
+ name = "Set linkspeed command"
+
+ @property
+ def ipmitool_args(self):
+ return ['cxoem', 'fabric', 'config', 'set', 'ls_policy',
+ self._params['ls_policy']]
+
+class GetUplinkCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric config get uplink command"""
+ name = "Get uplink command"
+ result_type = int
+
+ def parse_response(self, out, err):
+ if err:
+ raise IpmiError(err)
+ return int(out)
+
+ @property
+ def ipmitool_args(self):
+ return ['cxoem', 'fabric', 'config', 'get', 'uplink', 'interface',
+ self._params['iface']]
+
+class SetUplinkCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric config set uplink command"""
+ name = "Set uplink command"
+
+ @property
+ def ipmitool_args(self):
+ return ['cxoem', 'fabric', 'config', 'set', 'uplink',
+ self._params['uplink'], 'interface', self._params['iface']]
+
+class GetLinkUsersFactorCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric config get lu_factor command"""
+ name = "Get global link users factor command"
+ result_type = int
+
+ def parse_response(self, out, err):
+ if err:
+ raise IpmiError(err)
+ return int(out)
+
+ ipmitool_args = ['cxoem', 'fabric', 'config', 'get', 'lu_factor']
+
+class SetLinkUsersFactorCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool fabric config set lu_factor command"""
+ name = "Set global link users factor command"
+
+ @property
+ def ipmitool_args(self):
+ return ['cxoem', 'fabric', 'config', 'set', 'lu_factor',
+ self._params['lu_factor']]
+
+fabric_config_commands = {
+ "fabric_config_getipinfo" : GetIPInfoCommand,
+ "fabric_config_getmacaddresses" : GetMACAddressesCommand,
+ "fabric_config_updateconfig" : UpdateConfigCommand,
+ "fabric_config_getipsrc" : GetIPSrcCommand,
+ "fabric_config_setipsrc" : SetIPSrcCommand,
+ "fabric_config_factory_default" : FactoryDefaultCommand,
+ "fabric_config_get_ipaddr_base" : GetIPAddrBase,
+ "fabric_config_getlinkspeed" : GetLinkspeedCommand,
+ "fabric_config_setlinkspeed" : SetLinkspeedCommand,
+ "fabric_config_getlinkspeedpolicy" : GetLinkspeedPolicyCommand,
+ "fabric_config_setlinkspeedpolicy" : SetLinkspeedPolicyCommand,
+ "fabric_config_getuplinkinfo" : GetUplinkInfoCommand,
+ "fabric_config_getuplink" : GetUplinkCommand,
+ "fabric_config_setuplink" : SetUplinkCommand,
+ "fabric_config_getlinkusersfactor" : GetLinkUsersFactorCommand,
+ "fabric_config_setlinkusersfactor" : SetLinkUsersFactorCommand
+}
diff --git a/pyipmi/commands/freeipmi_pef.py b/pyipmi/commands/freeipmi_pef.py
new file mode 100644
index 0000000..a551d65
--- /dev/null
+++ b/pyipmi/commands/freeipmi_pef.py
@@ -0,0 +1,211 @@
+# 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.
+
+
+"""PEF related commands"""
+
+from .. import Command
+from pyipmi.freeipmi_pef import *
+from pyipmi.tools.responseparser import ResponseParserMixIn
+
+
+class FreeIPMIPEFInfoCommand(Command, ResponseParserMixIn):
+ """Describes the get PEF info IPMI command
+
+ This is "--info" to ipmi-pef-config
+ """
+ name = "Get PEF Info and Capabilities"
+ result_type = FreeIPMIPEFInfoResult
+
+ response_fields = {
+ 'PEF version' : {},
+ 'Alert action' : {},
+ 'Power down action' : {},
+ 'Power reset action' : {},
+ 'Power cycle action' : {},
+ 'OEM action' : {},
+ 'Diagnostic interrupt action' : {},
+ 'OEM event record filtering' : {},
+ 'Number of Event Filter Table entries' : {},
+ 'Number of Event Filters' : {},
+ 'Number of Alert Policy entries' : {},
+ 'Number of Alert Strings' : {}
+ }
+
+ ipmi_pef_config_args = ['--info']
+
+
+class FreeIPMIPEFCheckout(Command, ResponseParserMixIn):
+ """Retrieve platform event filtering configuration
+
+ """
+ name = "Checkout PEF Configuration"
+
+ def get_next_line(self, text):
+ line, c, rest = text.partition('\n')
+ line = line.strip()
+ return line, rest
+
+ def parse_section(self, rest):
+ config_dict = {}
+ line, rest = self.get_next_line(rest)
+ while line != "EndSection":
+ if line[0] == '#':
+ line, rest = self.get_next_line(rest)
+ continue
+ keyname, c, value = line.partition(" ")
+ config_dict[keyname.strip()] = value.strip()
+ line, rest = self.get_next_line(rest)
+ return config_dict, rest
+
+ def parse_results(self, response, err):
+ """Parse the output from "pef checkout." If a filename was given,
+ there will be no response
+ """
+
+ section_list = {}
+ line, rest = self.get_next_line(response)
+ while line != "":
+ if line[0] == '#':
+ line, rest = self.get_next_line(rest)
+ continue
+ keyword, c, value = line.partition(' ')
+ if keyword.strip() == "Section":
+ param_list, rest = self.parse_section(rest)
+ section_list[value.strip()] = param_list
+ line, rest = self.get_next_line(rest)
+ return section_list
+
+ @property
+ def ipmi_pef_config_args(self):
+ """
+ """
+ section = self._params.get("section")
+ filename = self._params.get('filename')
+ key = self._params.get('key')
+
+ if filename:
+ if section:
+ section = "--section=%s" % section
+ else:
+ section = ""
+ return ["--checkout", "--filename=" + filename, section]
+
+ if key and section:
+ return ["--checkout", "--key-pair=" + section + ":" + key]
+
+ if section:
+ return ["--checkout", "--section=" + section]
+
+ return ["--checkout"]
+
+
+class FreeIPMIPEFCommit(Command, ResponseParserMixIn):
+
+ """Update PEF configuration from file or key-value pair
+
+ """
+ name = "Update PEF Configuration"
+ result_type = FreeIPMIPEFCommitResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmi_pef_config_args(self):
+ """
+ """
+ filename = self._params.get('filename')
+
+ if filename:
+ return ["--commit", "--filename=" + filename]
+
+ key_value_pair = self._params.get('key_value_pair')
+ section = self._params.get('section')
+ if key_value_pair and section:
+ return ["--commit", "--key-pair=" + section + ":" + key_value_pair]
+
+ raise Exception("Command pef-config --commit requires either filename or key-value pair")
+
+
+class FreeIPMIPEFDiff(Command, ResponseParserMixIn):
+ """ Command to diff current PEF configuration against a file or key-value pair
+
+ """
+ name = "PEF Diff"
+ result_type = FreeIPMIDiffResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmi_pef_config_args(self):
+ """
+ """
+ filename = self._params.get('filename')
+ section = self._params.get('section')
+ key = self._params.get('key')
+
+ if filename:
+ if section:
+ section = "--section=%s" % section
+ else:
+ section = ""
+ return ["--diff", "--filename=" + filename, section]
+
+ if key and section:
+ return ["--diff", "--key-pair=" + section + ":" + key]
+
+ raise Exception("Command pef-config --diff requires either filename or key")
+
+
+class FreeIPMIPEFListSections(Command, ResponseParserMixIn):
+ """Describes the get PEF list sections command
+
+ """
+ name = "List PEF Table Sections"
+
+ def parse_results(self, response, err):
+ """Parse the output from "--listsections," which is a list
+ of section names separated by a single line break
+ """
+ result = map(lambda x: x.strip(), response.splitlines())
+ return result
+
+ ipmi_pef_config_args = ["--listsections"]
+
+
+freeipmi_pef_commands = {
+ 'pef_config_info' : FreeIPMIPEFInfoCommand,
+ 'pef_checkout' : FreeIPMIPEFCheckout,
+ 'pef_commit' : FreeIPMIPEFCommit,
+ 'pef_diff' : FreeIPMIPEFDiff,
+ 'pef_list_sections' : FreeIPMIPEFListSections
+}
diff --git a/pyipmi/commands/fru.py b/pyipmi/commands/fru.py
new file mode 100644
index 0000000..3eb3f87
--- /dev/null
+++ b/pyipmi/commands/fru.py
@@ -0,0 +1,132 @@
+# 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.
+
+
+"""FRU related commands"""
+
+from .. import Command
+from pyipmi.fru import *
+from pyipmi.tools.responseparser import ResponseParserMixIn
+
+
+class FRUPrintCommand(Command, ResponseParserMixIn):
+ """Describes the FRU get inventory area info IPMI command
+
+ This is "fru print" to ipmitool
+ """
+ name = "FRU Print"
+ result_type = FRUPrintResult
+
+ response_fields = {
+ 'FRU Device Description' : {},
+ 'Board Mfg Date' : {},
+ 'Board Mfg' : {},
+ 'Board Product' : {},
+ 'Board Serial' : {},
+ 'Board Part Number' : {},
+ 'Product Manufacturer' : {},
+ 'Product Name' : {},
+ 'Product Part Number' : {},
+ 'Product Serial' : {}
+ }
+
+ ipmitool_args = ["fru", "print"]
+
+
+class FRUReadCommand(Command, ResponseParserMixIn):
+ """Describes the FRU read IPMI command
+
+ This is "fru read" to ipmitool
+ """
+ name = "FRU Read"
+ result_type = FRUReadResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["fru", "read", self._params['fru_id'],
+ self._params['filename']]
+
+
+class FRUWriteCommand(Command, ResponseParserMixIn):
+ """Describes the FRU write IPMI command
+
+ This is "fru write" to ipmitool
+ """
+ name = "FRU Write"
+ result_type = FRUWriteResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["fru", "read", self._params['fru_id'],
+ self._params['filename']]
+
+
+class FRUUpgEKeyCommand(Command, ResponseParserMixIn):
+ """Describes the FRU upgEKey ipmitool command
+ """
+ name = "FRU UpgEkey"
+ result_type = FRUUpgEKeyResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["fru", "upgEkey", self._params['fru_id'],
+ self._params['filename']]
+
+
+class FRUShowCommand(Command, ResponseParserMixIn):
+ """Describes the ekanalyzer frushow ipmitool command
+ """
+ name = "FRU Show"
+ result_type = FRUShowResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["ekanalyzer", 'frushow',
+ 'oc=%s' % self._params['filename']]
+
+
+fru_commands = {
+ 'fru_print' : FRUPrintCommand,
+ 'fru_read' : FRUReadCommand,
+ 'fru_write' : FRUWriteCommand,
+ 'fru_upg_e_key' : FRUUpgEKeyCommand
+}
diff --git a/pyipmi/commands/fw.py b/pyipmi/commands/fw.py
new file mode 100644
index 0000000..0a70a8e
--- /dev/null
+++ b/pyipmi/commands/fw.py
@@ -0,0 +1,400 @@
+# 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 .. import Command, IpmiError
+from pyipmi.tools.responseparser import ResponseParserMixIn
+from pyipmi.fw import *
+
+class CommandWithErrors(Command, ResponseParserMixIn):
+
+ # TODO: Generalize this to base class? Better way to include
+ # error output in parsing?
+ def parse_response(self, out, err):
+ """Parse the response to a command
+
+ The 'ipmitool_response_format' attribute is used to determine
+ what parser to use to for interpreting the results.
+
+ Arguments:
+ out -- the text response of an command from stdout
+ err -- the text response of an command from stderr
+ """
+
+ out = out + err
+ return self.response_parser(out, err)
+
+class FWDownloadCommand(CommandWithErrors):
+ """Describes the cxoem fw download IPMI command
+
+ """
+
+ name = "Update a Firmware Image"
+ result_type = FWDownloadResult
+
+ response_fields = {
+ 'File Name' : {},
+ 'Partition' : {},
+ 'Slot' : {'attr': 'partition'},
+ 'Type' : {},
+ 'IP' : {},
+ 'TFTP Handle ID' : {},
+ 'Start FW download failed' : {'attr': 'fw_error'}
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["cxoem", "fw", "download", self._params['filename'],
+ self._params['partition'], self._params['image_type'],
+ "tftp", self._params['tftp_addr']]
+
+
+class FWUploadCommand(CommandWithErrors):
+ """Describes the cxoem fw upload IPMI command
+
+ """
+
+ name = "Retrieve Firmware From Device"
+ result_type = FWUploadResult
+
+ response_fields = {
+ 'File Name' : {},
+ 'Partition' : {},
+ 'Slot' : {'attr': 'partition'},
+ 'Type' : {},
+ 'IP' : {},
+ 'TFTP Handle ID' : {},
+ 'Start FW download failed' : {'attr': 'fw_error'}
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["cxoem", "fw", "upload", self._params['partition'],
+ self._params['filename'], self._params['image_type'],
+ "tftp", self._params['tftp_addr']]
+
+
+class FWRegisterReadCommand(CommandWithErrors):
+ """ cxoem fw register read command """
+ name = "Register Firmware Read"
+ result_type = FWRegisterReadResult
+
+ response_fields = {
+ 'File Name' : {},
+ 'Partition' : {},
+ 'Type' : {},
+ 'Error' : {}
+ }
+
+ def parse_response(self, out, err):
+ result = super(FWRegisterReadCommand, self).parse_response(out, err)
+ if hasattr(result, "error"):
+ raise IpmiError(result.error)
+
+ @property
+ def ipmitool_args(self):
+ return ["cxoem", "fw", "register", "read", self._params['partition'],
+ self._params['filename'], self._params['image_type']]
+
+
+class FWRegisterWriteCommand(CommandWithErrors):
+ """ cxoem fw register write command """
+ name = "Register Firmware Write"
+ result_type = FWRegisterWriteResult
+
+ response_fields = {
+ 'File Name' : {},
+ 'Partition' : {},
+ 'Type' : {},
+ 'Error' : {}
+ }
+
+ def parse_response(self, out, err):
+ result = super(FWRegisterWriteCommand, self).parse_response(out, err)
+ if hasattr(result, "error"):
+ raise IpmiError(result.error)
+
+ @property
+ def ipmitool_args(self):
+ return ["cxoem", "fw", "register", "write", self._params['partition'],
+ self._params['filename'], self._params['image_type']]
+
+
+class FWActivateCommand(CommandWithErrors):
+ """Describes the cxoem fw activate IPMI command
+
+ """
+
+ name = "Mark A Firmware Image As Active"
+ result_type = FWActivateResult
+
+ response_fields = {
+ "" : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["cxoem", "fw", "activate", self._params['partition']]
+
+
+class FWInvalidateCommand(CommandWithErrors):
+ """Describes the cxoem fw deactivate IPMI command
+
+ """
+
+ name = "Mark A Firmware Image As Inactive"
+ result_type = FWDeactivateResult
+
+ response_fields = {
+ "" : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["cxoem", "fw", "invalidate", self._params['partition']]
+
+
+class FWFlagsCommand(CommandWithErrors):
+ """Describes the cxoem fw flags IPMI command
+
+ """
+
+ name = "Set Flags For a Firmware Image"
+ result_type = FWFlagsResult
+
+ response_fields = {
+ "" : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["cxoem", "fw", "flags", self._params['partition'],
+ self._params['flags']]
+
+
+class FWStatusCommand(CommandWithErrors):
+ """Describes the cxoem fw status IPMI command
+
+ """
+
+ name = "Check Status of Most Recent Upload or Download"
+ result_type = FWStatus
+
+ response_fields = {
+ 'Status' : {},
+ 'Error' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["cxoem", "fw", "status", self._params['tftp_handle']]
+
+
+class FWCheckCommand(CommandWithErrors):
+ """Describes the cxoem fw check IPMI command
+
+ """
+
+ name = "Perform CRC of a Firmware Image"
+ result_type = FWCheckResult
+
+ response_fields = {
+ 'Partition' : {},
+ 'Slot' : {'attr': 'partition'},
+ 'CRC32' : {},
+ 'Error' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["cxoem", "fw", "check", self._params['partition']]
+
+
+class FWCancelCommand(CommandWithErrors):
+ """Describes the cxoem fw cancel IPMI command
+
+ """
+
+ name = "Cancel an In-Progress Upload or Download"
+ result_type = FWCancelResult
+
+ response_fields = {
+ "" : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["cxoem", "fw", "cancel", self._params['job_id']]
+
+
+class FWInfoCommand(CommandWithErrors):
+ """Describes the cxoem fw info IPMI command
+
+ """
+
+ name = "Request Firmware Information"
+ result_type = FWInfo
+ response_parser = ResponseParserMixIn.parse_colon_record_list
+
+ response_fields = {
+ "Partition" : {},
+ "Slot" : {"attr": "partition"},
+ "Type" : {},
+ "Offset" : {},
+ "Size" : {},
+ "Priority" : {},
+ "Daddr" : {},
+ "Flags" : {},
+ "In Use" : {},
+ "Version" : {},
+ "Error" : {}
+ }
+
+ ipmitool_args = ["cxoem", "fw", "info"]
+
+
+class FWGetCommand(CommandWithErrors):
+ """Describes the cxoem fw get IPMI command
+
+ """
+
+ name = "Retrieve Raw Firmware From Device"
+ result_type = FWGetResult
+
+ response_fields = {
+ 'File Name' : {},
+ 'Address' : {},
+ 'Size' : {},
+ 'IP' : {},
+ 'TFTP Handle ID' : {},
+ 'Start raw transfer failed' : {'attr': 'fw_error'}
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["cxoem", "fw", "get", self._params['filename'],
+ self._params['offset'], self._params['size'],
+ "tftp", self._params['tftp_addr']]
+
+
+class FWPutCommand(CommandWithErrors):
+ """Describes the cxoem fw put IPMI command
+
+ """
+
+ name = "Update Raw Firmware To Device"
+ result_type = FWPutResult
+
+ response_fields = {
+ 'File Name' : {},
+ 'Address' : {},
+ 'TSize' : {},
+ 'IP' : {},
+ 'TFTP Handle ID' : {},
+ 'Start raw transfer failed' : {'attr': 'fw_error'}
+ }
+
+ @property
+ def ipmitool_args(self):
+ """
+ """
+ return ["cxoem", "fw", "put", self._params['filename'],
+ self._params['offset'], self._params['size'],
+ "tftp", self._params['tftp_addr']]
+
+
+class FWResetCommand(CommandWithErrors):
+ """Describes the cxoem fw reset IPMI command
+
+ """
+
+ name = "Reset to factory default"
+ result_type = FWResetResult
+
+ response_fields = {
+ "Error" : {}
+ }
+
+ ipmitool_args = ["cxoem", "fw", "reset"]
+
+
+
+class FWVersionCommand(CommandWithErrors):
+ """Describes the cxoem fw version IPMI command
+
+ """
+
+ name = "Set the firmware version"
+ result_type = FWVersionResult
+
+ response_fields = {
+ "Error" : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["cxoem", "fw", "version", self._params['version']]
+
+
+fw_commands = {
+ "fw_download" : FWDownloadCommand,
+ "fw_upload" : FWUploadCommand,
+ "fw_register_read" : FWRegisterReadCommand,
+ "fw_register_write" : FWRegisterWriteCommand,
+ "fw_activate" : FWActivateCommand,
+ "fw_invalidate" : FWInvalidateCommand,
+ "fw_flags" : FWFlagsCommand,
+ "fw_status" : FWStatusCommand,
+ "fw_check" : FWCheckCommand,
+ "fw_cancel" : FWCancelCommand,
+ "fw_info" : FWInfoCommand,
+ "fw_get" : FWGetCommand,
+ "fw_put" : FWPutCommand,
+ "fw_reset" : FWResetCommand,
+ "fw_version" : FWVersionCommand
+}
diff --git a/pyipmi/commands/info.py b/pyipmi/commands/info.py
new file mode 100644
index 0000000..650e951
--- /dev/null
+++ b/pyipmi/commands/info.py
@@ -0,0 +1,92 @@
+# 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 .. import Command
+from pyipmi.info import *
+from pyipmi.tools.responseparser import ResponseParserMixIn
+from pyipmi import IpmiError
+
+class InfoBasicCommand(Command, ResponseParserMixIn):
+ """ Describes the cxoem info basic IPMI command
+ """
+
+ name = "Retrieve basic SoC info"
+
+ ipmitool_args = ["cxoem", "info", "basic"]
+
+ def parse_results(self, out, err):
+ """ Parse ipmitool output
+ """
+ result = InfoBasicResult()
+
+ if out.startswith("Calxeda SoC"):
+ for line in out.splitlines():
+ if line.lstrip().startswith("Calxeda SoC"):
+ result.iana = int(line.split()[2].strip("()"), 16)
+ elif line.lstrip().startswith("Firmware Version"):
+ result.firmware_version = line.partition(":")[2].strip()
+ elif line.lstrip().startswith("SoC Version"):
+ result.ecme_version = line.partition(":")[2].strip()
+ elif line.lstrip().startswith("Build Number"):
+ result.ecme_build_number = line.partition(":")[2].strip()
+ elif line.lstrip().startswith("Timestamp"):
+ result.ecme_timestamp = int(line.split()[1].strip(":()"))
+ elif err.startswith("Error: "):
+ raise IpmiError(err.splitlines()[0][7:])
+ else:
+ raise IpmiError("Unknown Error")
+
+ return result
+
+class InfoCardCommand(Command, ResponseParserMixIn):
+ """ Describes the cxoem info card IPMI command
+ """
+
+ name = "Retrieve card info"
+
+ ipmitool_args = ["cxoem", "info", "card"]
+
+ result_type = InfoCardResult
+ response_fields = {
+ 'Board Type' : {'attr' : 'type'},
+ 'Board Revision' : {'attr' : 'revision'}
+ }
+
+ def parse_results(self, out, err):
+ result = ResponseParserMixIn.parse_results(self, out, err)
+ if not (hasattr(result, 'type') and hasattr(result, 'revision')):
+ raise IpmiError(out.strip())
+ return result
+
+info_commands = {
+ "info_basic" : InfoBasicCommand,
+ "info_card" : InfoCardCommand
+}
diff --git a/pyipmi/commands/lan.py b/pyipmi/commands/lan.py
new file mode 100644
index 0000000..4316700
--- /dev/null
+++ b/pyipmi/commands/lan.py
@@ -0,0 +1,127 @@
+# 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.
+
+
+"""lan config related commands"""
+
+from .. import Command
+from pyipmi.lan import *
+from pyipmi.tools.responseparser import (ResponseParserMixIn,
+ str_to_list,
+ str_to_dict)
+
+
+class LANPrintCommand(Command, ResponseParserMixIn):
+ """Describes the lan print ipmitool command
+ """
+ name = "LAN Print"
+ result_type = LANPrintResults
+
+ def parse_response(self, out, err):
+ """ Strip out extraneous colons to allow more generic parsing
+ """
+ new_out_list = map(lambda x: x.lstrip(' \t\n:'), out.split('\n'))
+ new_out = reduce(lambda x, y: x + '\n' + y, new_out_list)
+
+ return self.response_parser(new_out, err)
+
+ def first_word_only(line):
+ y = line.split(' ')
+ return y[0]
+
+ response_fields = {
+ 'Set in Progress' : {},
+ 'Auth Type Support' : {'parser' : str_to_list},
+ 'Auth Type Enable' : {'lines' : 5,
+ 'parser' : str_to_dict,
+ 'operator' : ':',
+ 'delimiter' : '\n',
+ 'value_parser' : str_to_list},
+ 'IP Address Source' : {},
+ 'IP Address' : {},
+ 'Subnet Mask' : {},
+ 'MAC Address' : {},
+ 'SNMP Community String' : {},
+ 'IP Header' : {'parser' : str_to_dict,
+ 'operator' : '=',
+ 'delimiter' : ' '},
+ 'BMC ARP Control' : {'parser' : str_to_list,
+ 'delimiter' : ','},
+ 'Gratituous ARP Intrvl' : {'parser' : first_word_only},
+ 'Default Gateway IP' : {},
+ 'Default Gateway MAC' : {},
+ 'TFTP Server IP' : {},
+ 'NTP Server IP' : {},
+ 'NTP UDP port' : {},
+ 'OEM MAC0' : {},
+ 'OEM MAC1' : {},
+ 'OEM MAC2' : {},
+ 'OEM OUID' : {},
+ 'Supercluster OUID' : {},
+ 'Supercluster mode' : {},
+ 'Supercluster FID' : {},
+ '802.1q VLAN ID' : {},
+ '802.1q VLAN Priority' : {},
+ 'RMCP+ Cipher Suites' : {'parser' : str_to_list,
+ 'delimiter' : ','},
+ 'Cipher Suite Priv Max' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ channel = self._params.get('channel', '')
+ return ["lan", "print", channel]
+
+
+class LANSetCommand(Command, ResponseParserMixIn):
+ """Describes the ipmitool lan set command
+ """
+ name = "LAN Set"
+ result_type = LANSetResult
+
+ response_fields = {
+ }
+
+ @property
+ def ipmitool_args(self):
+ command = "lan set %s %s" % (self._params['channel'], self._params['command'])
+ command_array = command.split(' ')
+ params = self._params['param']
+ param_array = [params]
+ if self._params['command'] == 'auth':
+ param_array = params.split(' ')
+ command_array.extend(param_array)
+ return command_array
+
+
+lan_commands = {
+ 'lan_print' : LANPrintCommand,
+ 'lan_set' : LANSetCommand
+}
diff --git a/pyipmi/commands/mc.py b/pyipmi/commands/mc.py
new file mode 100644
index 0000000..b94c23b
--- /dev/null
+++ b/pyipmi/commands/mc.py
@@ -0,0 +1,69 @@
+# 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 .. import Command
+from pyipmi.tools.responseparser import ResponseParserMixIn
+from pyipmi.mc import *
+
+class CommandWithErrors(Command, ResponseParserMixIn):
+
+ def parse_response(self, out, err):
+ """Parse the response to a command
+
+ The 'ipmitool_response_format' attribute is used to determine
+ what parser to use to for interpreting the results.
+
+ Arguments:
+ out -- the text response of an command from stdout
+ err -- the text response of an command from stderr
+ """
+
+ out = out + err
+ return self.response_parser(out, err)
+
+class MCResetCommand(CommandWithErrors):
+ """ Describes the cxoem MC reset IPMI command
+ """
+
+ name = "Reset the MC"
+ result_type = MCResetResult
+
+ response_fields = {
+ "Error" : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["mc", "reset", self._params['mode']]
+
+mc_commands = {
+ "mc_reset" : MCResetCommand,
+}
diff --git a/pyipmi/commands/payload.py b/pyipmi/commands/payload.py
new file mode 100644
index 0000000..5d8df53
--- /dev/null
+++ b/pyipmi/commands/payload.py
@@ -0,0 +1,65 @@
+# 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.
+
+
+"""A series of wrappers around RMCP+ Payload commands"""
+
+from pyipmi import Command, InteractiveCommand, IpmiError
+from pyipmi.tools.responseparser import ResponseParserMixIn
+
+
+class ActivatePayloadCommand(InteractiveCommand, ResponseParserMixIn):
+ """Describes the Activate Payload command"""
+
+ #TODO: there could be other payload types
+
+ name = "Activate Payload"
+ ipmitool_args = ["-I", "lanplus", "-C", "0", "sol", "activate"]
+
+
+class DeactivatePayloadCommand(Command, ResponseParserMixIn):
+ """Describes the Deactivate Payload command"""
+
+ #TODO: there could be other payload types
+
+ def handle_command_error(self, out, err):
+ if err.find('Info: SOL payload already de-activated') > -1:
+ return
+
+ raise IpmiError(err)
+
+ name = "Deactivate Payload"
+ ipmitool_args = ["-I", "lanplus", "sol", "deactivate"]
+
+
+payload_commands = {
+ "activate_payload" : ActivatePayloadCommand,
+ "deactivate_payload" : DeactivatePayloadCommand
+}
diff --git a/pyipmi/commands/pef.py b/pyipmi/commands/pef.py
new file mode 100644
index 0000000..6221d1a
--- /dev/null
+++ b/pyipmi/commands/pef.py
@@ -0,0 +1,130 @@
+# 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.
+
+
+"""PEF related commands"""
+
+from .. import Command
+from pyipmi.pef import *
+from pyipmi.tools.responseparser import ResponseParserMixIn
+
+
+class PEFGetInfoCommand(Command, ResponseParserMixIn):
+ """Describes the get PEF info IPMI command
+
+ This is "pef info" to ipmitool
+ """
+ name = "Get PEF Capabilities"
+ result_type = PEFInfoResult
+
+ response_fields = {
+ 'Version' : {},
+ 'PEF table size' : {},
+ 'Alert policy table size' : {},
+ 'System GUID' : {},
+ 'Alert' : {},
+ 'Power-off' : {},
+ 'Reset' : {},
+ 'Power-cycle' : {},
+ 'OEM-defined' : {},
+ 'Diagnostic-interrupt' : {}
+ }
+
+ ipmitool_args = ["-v", "pef", "info"]
+
+
+class PEFGetPolicyCommand(Command, ResponseParserMixIn):
+ """Describes the get PEF policies ipmitool command
+
+ This is "pef policy" to ipmitool
+ """
+
+ name = "Get PEF Policy"
+ result_type = PEFPolicyResult
+
+ response_fields = {
+ }
+
+ ipmitool_args = ["pef", "policy"]
+
+
+class PEFGetStatusCommand(Command, ResponseParserMixIn):
+ """Describes the get PEF status ipmitool command
+
+ This is "pef status" to ipmitool
+ """
+ name = "Get PEF Status"
+ result_type = PEFStatusResult
+
+ response_fields = {
+ 'Last SEL addition' : {},
+ 'Last SEL record ID' : {},
+ 'Last S/W processed ID' : {},
+ 'Last BMC processed ID' : {},
+ 'PEF' : {},
+ 'PEF event messages' : {},
+ 'PEF startup delay' : {},
+ 'Alert startup delay' : {},
+ 'Alert' : {},
+ 'Power-off' : {},
+ 'Reset' : {},
+ 'Power-cycle' : {},
+ 'OEM-defined' : {},
+ 'Diagnostic-interrupt' : {}
+ }
+
+ ipmitool_args = ["pef", "status"]
+
+
+class PEFListEntriesCommand(Command, ResponseParserMixIn):
+ """Describes the get PEF list entries ipmitool command
+
+ This is "pef list" to ipmitool
+ """
+ name = "List PEF Entries"
+
+ response_parser = ResponseParserMixIn.parse_colon_record_list
+
+ result_type = PEFListResult
+
+ response_fields = {
+ 'PEF table entry' : {},
+ 'Status' : {}
+ }
+
+ ipmitool_args = ["-v", "pef", "list"]
+
+
+pef_commands = {
+ 'pef_get_info' : PEFGetInfoCommand,
+ 'pef_get_status' : PEFGetStatusCommand,
+ 'pef_get_policies' : PEFGetPolicyCommand,
+ 'pef_list_entries' : PEFListEntriesCommand
+}
diff --git a/pyipmi/commands/pet.py b/pyipmi/commands/pet.py
new file mode 100644
index 0000000..178a4e7
--- /dev/null
+++ b/pyipmi/commands/pet.py
@@ -0,0 +1,55 @@
+# 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.
+
+
+"""PEF related commands"""
+
+from .. import Command
+from pyipmi.pet import *
+from pyipmi.tools.responseparser import ResponseParserMixIn
+
+
+class PETAcknowledgeCommand(Command, ResponseParserMixIn):
+ """Describes the PET Acknowledge command
+
+ This is "--pet-acknowledge" to ipmi-pet
+ """
+ name = "Send a PET Acknowledge"
+ result_type = PETAcknowledgeResult
+
+ response_fields = {
+ }
+
+ ipmi_pet_args = ['--pet-acknowledge']
+
+
+pet_commands = {
+ 'pet_acknowledge' : PETAcknowledgeCommand
+}
diff --git a/pyipmi/commands/sdr.py b/pyipmi/commands/sdr.py
new file mode 100644
index 0000000..41e8f69
--- /dev/null
+++ b/pyipmi/commands/sdr.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.
+
+
+"""SDR related commands"""
+import re
+
+from .. import Command
+from .. sdr import Sdr, AnalogSdr
+from pyipmi.tools.responseparser import ResponseParserMixIn
+
+class SdrListCommand(Command, ResponseParserMixIn):
+ """Describes the sdr list command
+
+ This is not a single IPMI request type - it's an ipmitool
+ command that's composed of multiple IPMI requests.
+ """
+
+ name = 'SDR List'
+ result_type = Sdr
+
+ response_parser = ResponseParserMixIn.parse_colon_record_list
+ ipmitool_args = ['-v', 'sdr', 'list', 'all']
+
+ def sensor_name_parser(string):
+ return string.split('(')[0].strip()
+
+ def entity_id_parser(string):
+ m = re.search('(\d.\d{1,2})', string)
+ return m.groups()[0]
+
+ def get_response_types(self, record):
+ """Only matches Analog sensors right now.
+
+ There are several more types of records to match, if they
+ are needed.
+ """
+ if re.search('Sensor Type \(Analog\)', record):
+ return AnalogSdr, self.analog_response_fields
+ else:
+ return None, None
+
+ """
+ Unparsed fields for analog sensors:
+
+ Readable Thresholds : lnr lcr lnc unc ucr unr
+ Settable Thresholds : lnr lcr lnc unc ucr unr
+ Threshold Read Mask : lnr lcr lnc unc ucr unr
+ Assertion Events :
+ Assertions Enabled : unc+ ucr+ unr+
+ Deassertions Enabled : unc+ ucr+ unr+
+ """
+ analog_response_fields = {
+ 'Sensor ID' : {
+ 'attr' : 'sensor_name',
+ 'parser' : sensor_name_parser
+ },
+ 'Entity ID' : {
+ 'attr' : 'entity_id',
+ 'parser' : entity_id_parser
+ },
+ 'Sensor Type (Analog)' : { 'attr' : 'sensor_type' },
+ 'Sensor Reading' : {},
+ 'Status' : {},
+ 'Nominal Reading' : {},
+ 'Normal Minimum' : {},
+ 'Normal Maximum' : {},
+ 'Upper non-recoverable' : {},
+ 'Upper critical' : {},
+ 'Upper non-critical' : {},
+ 'Lower non-recoverable' : {},
+ 'Lower critical' : {},
+ 'Lower non-critical' : {},
+ 'Positive Hysteresis' : {},
+ 'Negative Hysteresis' : {},
+ 'Minimum sensor range' : {},
+ 'Maximum sensor range' : {},
+ 'Event Message Control' : {},
+ }
+
+sdr_commands = {
+ "get_sdr_list" : SdrListCommand
+}
diff --git a/pyipmi/commands/sel.py b/pyipmi/commands/sel.py
new file mode 100644
index 0000000..847468d
--- /dev/null
+++ b/pyipmi/commands/sel.py
@@ -0,0 +1,229 @@
+# 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.
+
+
+"""This module contains a series of wrappers
+ around SEL commands for ipmitool
+"""
+
+import os
+import re
+import tempfile
+import string
+from pyipmi import Command, IpmiError
+from pyipmi.tools.responseparser import ResponseParserMixIn, str2bool
+from pyipmi.sel import (SELTimestamp, SELInfo, SELAllocInfo, SELRecord,
+ SELOverflowError, SELTimestampError)
+
+
+class SELTimeSetCommand(Command, ResponseParserMixIn):
+ """Describes the Set SEL Time command"""
+
+ name = "Set SEL Time"
+ # TODO: get response data from ipmitool
+
+ @property
+ def ipmitool_args(self):
+ """return args for ipmitool command"""
+ return ["sel", "time", "set", self._params["time"].timestamp]
+
+ def handle_command_error(self, resp, err):
+ raise SELTimestampError(err)
+
+
+class SELTimeGetCommand(Command, ResponseParserMixIn):
+ """Describes the Get SEL Time command"""
+
+ def response_parser(self, resp, err):
+ """A helper function to parse a timestamp returned from
+ an 'sel time get' command
+ """
+
+ return SELTimestamp(resp.strip())
+
+ name = "Get SEL Time"
+ result_type = SELTimestamp
+ ipmitool_args = ["sel", "time", "get"]
+
+
+class SELInfoCommand(Command, ResponseParserMixIn):
+ """Describes the Get SEL Info command"""
+
+ def version_parser(string):
+ vdict = {}
+ vregex = '(?P<number>[12]\.[05]) \((?P<compliant>.+) compliant\)'
+ match = re.match(vregex, string)
+ vdict['number'] = match.group('number')
+ vdict['compliant'] = match.group('compliant').split(', ')
+ return vdict
+
+ name = "SEL Info"
+ ipmitool_args = ["sel", "info"]
+ result_type = SELInfo
+
+ response_fields = {
+ 'Version' : {'parser': version_parser},
+ 'Entries' : {'parser': lambda s: int(s)},
+ 'Free Space' : {'parser': lambda s: int(s[:-6])}, #removes ' bytes'
+ 'Last Add Time' : {'parser': lambda ts: SELTimestamp(ts)},
+ 'Last Del Time' : {'parser': lambda ts: SELTimestamp(ts)},
+ 'Overflow' : {'parser': str2bool},
+ 'Supported Cmds' : {'parser': lambda s: re.findall("\w[ \w]+", s)}
+ }
+
+
+class SELAllocInfoCommand(Command, ResponseParserMixIn):
+ """Describes the sel alloc info command"""
+
+ name = "SEL Alloc Info"
+ ipmitool_args = ["sel", "info"]
+ result_type = SELAllocInfo
+
+ response_fields = {
+ '# of Alloc Units' : {'attr': 'num_alloc_units', 'parser': lambda s: int(s)},
+ 'Alloc Unit Size' : {'parser': lambda s: int(s)},
+ '# Free Units' : {'attr': 'num_free_units', 'parser': lambda s: int(s)},
+ 'Largest Free Blk' : {'parser': lambda s: int(s)},
+ 'Max Record Size' : {'parser': lambda s: int(s)}
+ }
+
+
+class SELAddCommand(Command, ResponseParserMixIn):
+ """Describes the sel add command"""
+
+ #TODO: get response data from ipmitool
+
+ name = "SEL Add"
+
+ def __init__(self, *args, **kwargs):
+ super(SELAddCommand, self).__init__(*args, **kwargs)
+ self._tmpfile = tempfile.NamedTemporaryFile(delete=False)
+
+ for e in self._params['records']:
+ # TODO: handle other types of SEL Records
+ # TODO: allow for malformed SEL entries
+ entry = ('%s %s %s %s %s %s %s' %
+ (hex(e.evm_rev), hex(e.sensor_type), hex(e.sensor_number),
+ hex((e.event_direction << 7) | e.event_type),
+ hex(e.event_data[0]), hex(e.event_data[1]), hex(e.event_data[2])))
+ self._tmpfile.write(str(entry) + '\n')
+ self._tmpfile.flush()
+
+ def __del__(self):
+ os.remove(self._tmpfile.name)
+
+ @property
+ def ipmitool_args(self):
+ """return args for ipmitool command"""
+ return ["sel", "add", self._tmpfile.name]
+
+ def handle_command_error(self, resp, err):
+ if err.find('Out of space') > 0:
+ raise SELOverflowError(err)
+
+ raise IpmiError(err)
+
+
+class SELGetCommand(Command, ResponseParserMixIn):
+ """Describes the sel get command"""
+
+ def event_data_parser(string):
+ data = int(string, 16)
+ return (data >> 16, (data >> 8) & 0xff, data & 0xff)
+
+ def response_parser(self, response, err):
+ if err.find("command failed") > 0:
+ return None
+
+ entry = self.parse_colon_record(response, err)
+ entry.normalize()
+ return entry
+
+
+ direction_parser = lambda d: 0 if d == 'Assertion Event' else 1
+ hex_parser = lambda x: int(x, 16)
+
+ name = "SEL Get"
+ result_type = SELRecord
+
+ @property
+ def ipmitool_args(self):
+ """return args for ipmitool command"""
+ return ["sel", "get"] + list(self._params['record_ids'])
+
+ # TODO: add support for oem records
+ response_fields = {
+ 'SEL Record ID': {'parser': hex_parser, 'attr': 'record_id'},
+ 'Record Type' : {'parser': hex_parser},
+ 'Timestamp': {},
+ 'Generator ID': {'parser': hex_parser},
+ 'EvM Revision': {'parser': hex_parser},
+ 'Sensor Type': {}, #TODO: covert to hex code
+ 'Sensor Number': {'parser': hex_parser},
+ 'Event Type': {}, #TODO: convert to hex code
+ 'Event Direction': {'parser': direction_parser},
+ 'Event Data': {'parser': event_data_parser},
+ 'Description': {}
+ }
+
+
+class SELClearCommand(Command, ResponseParserMixIn):
+ """Describes the Clear SEL command"""
+
+ name = "Clear SEL"
+ ipmitool_args = ["sel", "clear"]
+ # TODO: get response data from ipmitool
+
+
+class SELListCommand(Command, ResponseParserMixIn):
+ """Describes SEL List command
+ note: this command is non-standard
+ """
+
+ def response_parser(self, resp, err):
+ sel_list = resp.strip().split('\n')
+ sel_list = map(string.strip, sel_list)
+ return filter(lambda s: s != '', sel_list) # remove blank entries
+
+ name = "List SEL"
+ ipmitool_args = ["sel", "list"]
+ result_type = list
+
+
+sel_commands = {
+ "set_sel_time" : SELTimeSetCommand,
+ "get_sel_time" : SELTimeGetCommand,
+ "sel_info" : SELInfoCommand,
+ "sel_alloc_info" : SELAllocInfoCommand,
+ "sel_add" : SELAddCommand,
+ "sel_get" : SELGetCommand,
+ "sel_clear" : SELClearCommand,
+ "sel_list" : SELListCommand
+}
diff --git a/pyipmi/commands/sol.py b/pyipmi/commands/sol.py
new file mode 100644
index 0000000..98fdfd8
--- /dev/null
+++ b/pyipmi/commands/sol.py
@@ -0,0 +1,207 @@
+# 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.
+
+
+"""A series of wrappers around SOL commands"""
+
+from pyipmi import Command, IpmiError
+from pyipmi.tools.responseparser import ResponseParserMixIn, str2bool
+
+
+def bit_rate_parser(bit_rate):
+ """Parse a bit rate returned from ipmitool's "sol info" command"""
+ assert bit_rate in ('IPMI-Over-Serial-Setting', '9.6', '19.2',
+ '38.4', '57.6', '115.2')
+
+ if bit_rate != 'IPMI-Over-Serial-Setting':
+ bit_rate = float(bit_rate)
+ #TODO: maybe find the IPMI-Over-Serial-Setting?
+
+ return bit_rate
+
+def channel_parser(channel):
+ """Parse a channel returned from ipmitool's "sol info" command.
+ Channel format is: "%d (%x)" % (channel, channel)
+ """
+
+ chan, xchan = channel.split(' (')
+ return int(chan)
+
+def bool2str(boolval):
+ """Map True to 'true', False to 'false'"""
+ return str(boolval).lower()
+
+def priv_level_formatter(level):
+ """Format privilege level"""
+ level = level.lower()
+ if level == "administrator":
+ level = "admin"
+ return level
+
+# need to know four ipmitool-specific things about each configuration parameter
+# set_name: name used in ipmitool's "sol set" command
+# get_name: name printed from ipmitool's "sol info" command
+# parser: a function to parse the value printed by ipmitool's "sol info" command
+# formatter: formatter tool-independant values into formats required by tool
+IPMITOOL_SOL_PARAMETERS = {
+ 'set_in_progress': {
+ 'set_name' : 'set-in-progress',
+ 'get_name' : 'Set in progress',
+ 'parser' : lambda s: s.replace('-', '_'),
+ 'formatter' : lambda s: s.replace('_', '-'),
+ },
+ 'enable' : {
+ 'set_name' : 'enabled',
+ 'get_name' : 'Enabled',
+ 'parser' : str2bool,
+ 'formatter' : bool2str,
+ },
+ 'force_encryption' : {
+ 'set_name' : 'force-encryption',
+ 'get_name' : 'Force Encryption',
+ 'parser' : str2bool,
+ 'formatter' : bool2str,
+ },
+ 'force_authentication' : {
+ 'set_name' : 'force-authentication',
+ 'get_name' : 'Force Authentication',
+ 'parser' : str2bool,
+ 'formatter' : bool2str,
+ },
+ 'privilege_level' : {
+ 'set_name' : 'privilege-level',
+ 'get_name' : 'Privilege Level',
+ 'parser' : str,
+ 'formatter' : priv_level_formatter,
+ },
+ 'character_accumulate_interval' : {
+ 'set_name' : 'character-accumulate-level',
+ 'get_name' : 'Character Accumulate Level (ms)',
+ 'parser' : lambda s: int(s) / 5,
+ 'formatter' : str,
+ },
+ 'character_send_threshold' : {
+ 'set_name' : 'character-send-threshold',
+ 'get_name' : 'Character Send Threshold',
+ 'parser' : int,
+ 'formatter' : str,
+ },
+ 'retry_count' : {
+ 'set_name' : 'retry-count',
+ 'get_name' : 'Retry Count',
+ 'parser' : int,
+ 'formatter' : str,
+ },
+ 'retry_interval' : {
+ 'set_name' : 'retry-interval',
+ 'get_name' : 'Retry Interval (ms)',
+ 'parser' : lambda s: int(s) / 10,
+ 'formatter' : str,
+ },
+ 'volatile_bit_rate' : {
+ 'set_name' : 'volatile-bit-rate',
+ 'get_name' : 'Volatile Bit Rate (kbps)',
+ 'parser' : bit_rate_parser,
+ 'formatter' : str,
+ },
+ 'non_volatile_bit_rate' : {
+ 'set_name' : 'non-volatile-bit-rate',
+ 'get_name' : 'Non-Volatile Bit Rate (kbps)',
+ 'parser' : bit_rate_parser,
+ 'formatter' : str,
+ },
+ 'payload_channel' : {
+ 'set_name' : None,
+ 'get_name' : 'Payload Channel',
+ 'parser' : channel_parser,
+ 'formatter' : None,
+ },
+ 'payload_port_number' : {
+ 'set_name' : None,
+ 'get_name' : 'Payload Port',
+ 'parser' : int,
+ 'formatter' : None,
+ }
+}
+
+# TODO: why does atom only work with lanplus, but qemu works on the lan iface?
+# TODO: enable/disable encryption
+IPMITOOL_SOL_ARGS = ["-I", "lanplus", "-C", "0", "sol"]
+
+class SetSOLConfigurationParametersCommand(Command, ResponseParserMixIn):
+ """Describes the Set SOL Configuration Parameters command"""
+
+ # ipmitool handles setting set-in-progress/set-complete for us
+ # however, other tools might not. this is where that'd be handled
+
+ name = "Set SOL Configuration Parameters"
+
+ @property
+ def ipmitool_args(self):
+ param = self._params['param']
+ ipmitool_param = IPMITOOL_SOL_PARAMETERS[param]['set_name']
+ formatter = IPMITOOL_SOL_PARAMETERS[param]['formatter']
+ val = formatter(self._params['value'])
+
+ if param is None:
+ raise IpmiError('ipmitool does not support "sol set %s" ' % param)
+
+ return IPMITOOL_SOL_ARGS + ["set", ipmitool_param, val, 'noguard']
+
+
+class GetSOLConfigurationParametersCommand(Command, ResponseParserMixIn):
+ """Describes the Get SOL Configuration Parameters command"""
+
+ name = "Get SOL Configuration Parameters"
+ ipmitool_args = IPMITOOL_SOL_ARGS + ["info"]
+
+ def parse_results(self, response, err):
+ """Parse the output from "sol info" for the desired parameters,
+ format the result, and return it.
+ """
+ response = response.split('\n')
+ result = {}
+
+ params = self._params['params']
+ for param in params:
+ field = IPMITOOL_SOL_PARAMETERS[param]['get_name']
+ parse = IPMITOOL_SOL_PARAMETERS[param]['parser']
+
+ field_value, = filter(lambda s: s.find(field) == 0, response)
+ field, value = field_value.split(': ')
+ result[param] = parse(value)
+
+ return result
+
+
+sol_commands = {
+ "set_sol_config_params" : SetSOLConfigurationParametersCommand,
+ "get_sol_config_params" : GetSOLConfigurationParametersCommand
+}
diff --git a/pyipmi/commands/user.py b/pyipmi/commands/user.py
new file mode 100644
index 0000000..6ecdbe1
--- /dev/null
+++ b/pyipmi/commands/user.py
@@ -0,0 +1,163 @@
+# 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.
+
+
+"""user management IPMI commands"""
+
+from .. import Command
+from pyipmi.user import *
+from pyipmi.tools.responseparser import (ResponseParserMixIn,
+ str_to_list,
+ str_to_dict)
+
+
+class UserListCommand(Command, ResponseParserMixIn):
+ """Describes the user list ipmitool command
+ """
+ name = "User List"
+ result_type = UserListResults
+
+ def parse_response(self, out, err):
+ """ Output is a table with a header row:
+ ID Name Callin Link Auth IPMI Msg Channel Priv Limit
+ 1 anonymous true false false NO ACCESS
+ """
+ result = {}
+ for line in out.strip().split('\n')[1:]:
+ user_info = self.result_type()
+
+ user_info_list = line.strip().split()
+ if len(user_info_list) < 5:
+ continue
+
+ key = line[0:3].strip()
+ user_info.name = line[4:20].strip()
+ user_info.callin = line[22:28].strip()
+ user_info.link_auth = line[29:39].strip()
+ user_info.ipmi_msg = line[40:48].strip()
+ user_info.channel_priv_limit = line[51:].strip()
+
+ result[key] = user_info
+
+ return result
+
+ @property
+ def ipmitool_args(self):
+ channel = self._params.get('channel', '')
+ return ["user", "list", channel]
+
+
+class UserSetNameCommand(Command, ResponseParserMixIn):
+ """Describes the user set name ipmitool command
+ """
+ name = "User Set Name"
+ result_type = UserSetNameResults
+
+ response_fields = {
+ 'Field Name' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["user", "set", "name", self._params['userid'],
+ self._params['name']]
+
+
+class UserSetPasswordCommand(Command, ResponseParserMixIn):
+ """Describes the user set password ipmitool command
+ """
+ name = "User Set Password"
+ result_type = UserSetPasswordResults
+
+ response_fields = {
+ 'Field Name' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ password = self._params.get('password', '')
+ return ["user", "set", "password", self._params['userid'], password]
+
+
+class UserDisableCommand(Command, ResponseParserMixIn):
+ """Describes the user disable ipmitool command
+ """
+ name = "User Disable"
+ result_type = UserDisableResults
+
+ response_fields = {
+ 'Field Name' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["user", "disable", self._params['userid']]
+
+
+class UserEnableCommand(Command, ResponseParserMixIn):
+ """Describes the user enable ipmitool command
+ """
+ name = "User Enable"
+ result_type = UserEnableResults
+
+ response_fields = {
+ 'Field Name' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ return ["user", "enable", self._params['userid']]
+
+
+class UserPrivCommand(Command, ResponseParserMixIn):
+ """Describes the user priv ipmitool command
+ """
+ name = "User Set Privileges"
+ result_type = UserPrivResults
+
+ response_fields = {
+ 'Field Name' : {}
+ }
+
+ @property
+ def ipmitool_args(self):
+ channel = self._params.get('channel', '')
+ return ["user", "priv", self._params['userid'],
+ self._params['priv_level'], channel]
+
+
+user_commands = {
+ 'user_list' : UserListCommand,
+ 'user_set_name' : UserSetNameCommand,
+ 'user_set_password' : UserSetPasswordCommand,
+ 'user_enable' : UserEnableCommand,
+ 'user_disable' : UserDisableCommand,
+ 'user_priv' : UserPrivCommand
+}
diff --git a/pyipmi/commands/watchdog.py b/pyipmi/commands/watchdog.py
new file mode 100644
index 0000000..1f3c5c0
--- /dev/null
+++ b/pyipmi/commands/watchdog.py
@@ -0,0 +1,92 @@
+# 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.
+
+
+"""watchdog related commands"""
+
+from .. import Command
+from pyipmi.watchdog import *
+from pyipmi.tools.responseparser import ResponseParserMixIn
+
+
+class WatchdogGetCommand(Command, ResponseParserMixIn):
+ """Describes the watchdog get IPMI command
+
+ This is "mc watchdog get" to ipmitool
+ """
+ name = "Watchdog Get"
+ result_type = WatchdogGetResult
+
+ response_fields = {
+ 'Watchdog Timer Use' : {},
+ 'Watchdog Timer Is' : {},
+ 'Watchdog Timer Actions' : {},
+ 'Pre-timeout interval' : {},
+ 'Timer Expiration Flags' : {},
+ 'Initial Countdown' : {},
+ 'Present Countdown' : {}
+ }
+
+ ipmitool_args = ["mc", "watchdog", "get"]
+
+
+class WatchdogResetCommand(Command, ResponseParserMixIn):
+ """Describes the watchdog rest IPMI command
+
+ This is "mc watchdog reset" to ipmitool
+ """
+ name = "Watchdog Reset"
+ result_type = WatchdogResetResult
+
+ response_fields = {
+ }
+
+ ipmitool_args = ["mc", "watchdog", "reset"]
+
+
+class WatchdogOffCommand(Command, ResponseParserMixIn):
+ """Describes the watchdog off IPMI command
+
+ This is "mc watchdog off" to ipmitool
+ """
+ name = "Watchdog Off"
+ result_type = WatchdogOffResult
+
+ response_fields = {
+ }
+
+ ipmitool_args = ["mc", "watchdog", "off"]
+
+
+watchdog_commands = {
+ 'watchdog_get' : WatchdogGetCommand,
+ 'watchdog_reset' : WatchdogResetCommand,
+ 'watchdog_off' : WatchdogOffCommand
+}
diff --git a/pyipmi/data.py b/pyipmi/data.py
new file mode 100644
index 0000000..6bf7831
--- /dev/null
+++ b/pyipmi/data.py
@@ -0,0 +1,48 @@
+# 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.
+
+
+"""Data results"""
+
+class DataMemReadResult(object):
+ """Object to hold data mem read results"""
+ pass
+
+class DataMemWriteResult(object):
+ """Object to hold data mem write results"""
+ pass
+
+class DataCDBReadResult(object):
+ """Object to hold data cdb read results"""
+ pass
+
+class DataCDBWriteResult(object):
+ """Object to hold data cdb write results"""
+ pass
diff --git a/pyipmi/dcmi.py b/pyipmi/dcmi.py
new file mode 100644
index 0000000..b66d40c
--- /dev/null
+++ b/pyipmi/dcmi.py
@@ -0,0 +1,92 @@
+# 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.
+
+
+"""DCMI records"""
+
+class DCMIGetCapabilitiesResult(object):
+ """Object to hold DCMI capabilities request results"""
+ pass
+
+
+class DCMISetAssetTagResult(object):
+ """Object to hold DCMI set asset tag results"""
+ pass
+
+
+class DCMIGetAssetTagResult(object):
+ """Object to hold DCMI get asset tag results"""
+ pass
+
+
+class DCMIGetManagementControllerIDResult(object):
+ """Object to hold DCMI get management controller ID result"""
+ pass
+
+class DCMISetManagementControllerIDResult(object):
+ """Object to hold DCMI get management controller ID result"""
+ pass
+
+class DCMIGetSensorInfoResult(object):
+ """Object to hold DCMI get sensor info result"""
+ pass
+
+class DCMIGetPowerStatisticsResult(object):
+ """Object to hold DCMI power statistics result"""
+ pass
+
+
+class DCMIGetPowerLimitResult(object):
+ """Object to hold DCMI get power limit result"""
+ pass
+
+
+class DCMISetPowerLimitResult(object):
+ """Object to hold DCMI set power limit result"""
+ pass
+
+
+class DCMIPowerLimitRequestedResult(object):
+ """Object to hold DCMI power limit requested result"""
+ pass
+
+
+class DCMIActivatePowerLimitResult(object):
+ """Object to hold DCMI activate power limit result"""
+ pass
+
+class DCMICorrectionTimeLimitResult(object):
+ """Object to hold DCMI correcttion time limit result"""
+ pass
+
+class DCMIStatisticsSamplingPeriodResult(object):
+ """Object to hold DCMI Statistic Sampling result"""
+ pass
+
diff --git a/pyipmi/event.py b/pyipmi/event.py
new file mode 100644
index 0000000..4d7545d
--- /dev/null
+++ b/pyipmi/event.py
@@ -0,0 +1,41 @@
+# 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.
+
+
+"""ipmitool test event Results"""
+
+class GenericEventResult(object):
+ """Object to hold generic event generate results"""
+ pass
+
+
+class AssertSensorEventResult(object):
+ """Object to hold sensor event generate results"""
+ pass
diff --git a/pyipmi/fabric.py b/pyipmi/fabric.py
new file mode 100644
index 0000000..059ea06
--- /dev/null
+++ b/pyipmi/fabric.py
@@ -0,0 +1,63 @@
+# 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.
+
+
+"""Fabric results"""
+
+class FabricGetIPInfoResult(object):
+ """Object to hold fabric ip list results"""
+ pass
+
+class FabricGetMACAddressesResult(object):
+ """Object to hold fabric mac list results"""
+ pass
+
+class FabricUpdateConfigResult(object):
+ """Object to hold update config list results"""
+
+class FabricGetUplinkInfoResult(object):
+ """Object to hold fabric uplink_info results"""
+ pass
+
+class FabricGetLinkStatsResult(object):
+ """Object to hold the fabric link_stats results"""
+ pass
+
+class FabricGetLinkMapResult(object):
+ """Object to hold the fabric linkmap results"""
+ pass
+
+class FabricGetRoutingTableResult(object):
+ """Object to hold the fabric routing_table results"""
+ pass
+
+class FabricGetDepthChartResult(object):
+ """Object to hold the fabric depth_chart results"""
+ pass
diff --git a/pyipmi/freeipmi_pef.py b/pyipmi/freeipmi_pef.py
new file mode 100644
index 0000000..531c21d
--- /dev/null
+++ b/pyipmi/freeipmi_pef.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.
+
+
+"""PEF Results"""
+
+class FreeIPMIPEFInfoResult(object):
+ """Object to hold PEF info results"""
+ pass
+
+
+class FreeIPMIPEFCheckoutResult(object):
+ """Object to hold PEF checkout results"""
+ pass
+
+
+class FreeIPMIPEFCommitResult(object):
+ """Object to hold PEF commit results"""
+ pass
+
+
+class FreeIPMIDiffResult(object):
+ """Object to hold PEF diff results"""
+ pass
+
+
+class FreeIPMIPEFListSectionsResult(object):
+ """Object to hold PEF list sections results"""
+ pass
diff --git a/pyipmi/fru.py b/pyipmi/fru.py
new file mode 100644
index 0000000..1266ee8
--- /dev/null
+++ b/pyipmi/fru.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.
+
+
+"""ipmitool fru Results"""
+
+class FRUPrintResult(object):
+ """Object to hold fru print results"""
+ pass
+
+
+class FRUReadResult(object):
+ """Object to hold fru read results"""
+ pass
+
+
+class FRUWriteResult(object):
+ """Object to hold fru write results"""
+ pass
+
+
+class FRUUpgEKeyResult(object):
+ """Object to hold fru upgEkey results"""
+ pass
+
+
+class FRUShowResult(object):
+ """Object to hold fru show results"""
+ pass
diff --git a/pyipmi/fw.py b/pyipmi/fw.py
new file mode 100644
index 0000000..37a779a
--- /dev/null
+++ b/pyipmi/fw.py
@@ -0,0 +1,115 @@
+# 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.
+
+
+"""FW records"""
+
+class FWInfo(object):
+ """Object to hold device-reported SPI flash table"""
+ error = None
+
+ def __str__(self):
+ return "\n".join("%s: %r" % (x, getattr(self, x))
+ for x in ["partition", "type", "offset", "size", "priority",
+ "daddr", "flags", "version", "in_use"])
+
+ def __eq__(self, other):
+ if isinstance(other, self.__class__):
+ return vars(self) == vars(other)
+ else:
+ return False
+
+
+class FWDownloadResult(object):
+ """Object to hold firmware update results"""
+ fw_error = None
+
+
+class FWUploadResult(object):
+ """Object to hold firmware retrieve results"""
+ fw_error = None
+
+
+class FWRegisterReadResult(object):
+ pass
+
+
+class FWRegisterWriteResult(object):
+ pass
+
+
+class FWActivateResult(object):
+ """Object to hold firmware activate results"""
+ pass
+
+
+class FWDeactivateResult(object):
+ """Object to hold firmware deactivate results"""
+ pass
+
+
+class FWFlagsResult(object):
+ """Object to hold firmware flag command results"""
+ pass
+
+
+class FWStatus(object):
+ """Object to hold firmware operation status"""
+ error = None
+
+
+class FWCancelResult(object):
+ """Object to hold firmware operation cancelation results"""
+ pass
+
+
+class FWCheckResult(object):
+ """Object to hold firmware CRC check results"""
+ error = None
+
+
+class FWGetResult(object):
+ """Object to hold firmware get results"""
+ fw_error = None
+
+
+class FWPutResult(object):
+ """Object to hold firmware put results"""
+ fw_error = None
+
+
+class FWResetResult(object):
+ """Object to hold firmware reset results"""
+ pass
+
+class FWVersionResult(object):
+ """Object to hold firmware version results"""
+ pass
+
diff --git a/pyipmi/info.py b/pyipmi/info.py
new file mode 100644
index 0000000..bae6bda
--- /dev/null
+++ b/pyipmi/info.py
@@ -0,0 +1,41 @@
+# 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.
+
+
+"""info records"""
+
+class InfoBasicResult(object):
+ """Object to hold info basic results"""
+ def __str__(self):
+ return "\n".join("%s: %r" % x for x in sorted(vars(self).iteritems()))
+
+class InfoCardResult(object):
+ """Object to hold info card results"""
+ pass
diff --git a/pyipmi/lan.py b/pyipmi/lan.py
new file mode 100644
index 0000000..ba8a850
--- /dev/null
+++ b/pyipmi/lan.py
@@ -0,0 +1,41 @@
+# 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.
+
+
+"""ipmitool lan Results"""
+
+class LANPrintResults(object):
+ """Object to hold lan print results"""
+ pass
+
+
+class LANSetResult(object):
+ """Object to hold lan set results"""
+ pass
diff --git a/pyipmi/mc.py b/pyipmi/mc.py
new file mode 100644
index 0000000..ca9b439
--- /dev/null
+++ b/pyipmi/mc.py
@@ -0,0 +1,36 @@
+# 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.
+
+
+"""MC records"""
+
+class MCResetResult(object):
+ """Object to hold MC reset results"""
+ pass
diff --git a/pyipmi/pef.py b/pyipmi/pef.py
new file mode 100644
index 0000000..32b7e12
--- /dev/null
+++ b/pyipmi/pef.py
@@ -0,0 +1,52 @@
+# 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.
+
+
+"""PEF Results"""
+
+class PEFInfoResult(object):
+ """Object to hold PEF info results"""
+ pass
+
+
+class PEFPolicyResult(object):
+ """Object to hold PEF policy results"""
+ pass
+
+
+class PEFStatusResult(object):
+ """Object to hold PEF status results"""
+ pass
+
+
+class PEFListResult(object):
+ """Object to hold PEF list results"""
+ pass
+
diff --git a/pyipmi/pet.py b/pyipmi/pet.py
new file mode 100644
index 0000000..291d423
--- /dev/null
+++ b/pyipmi/pet.py
@@ -0,0 +1,36 @@
+# 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.
+
+
+"""PET Results"""
+
+class PETAcknowledgeResult(object):
+ """Object to hold PET Acknowledge results"""
+ pass
diff --git a/pyipmi/sdr.py b/pyipmi/sdr.py
new file mode 100644
index 0000000..89a7fba
--- /dev/null
+++ b/pyipmi/sdr.py
@@ -0,0 +1,49 @@
+# 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.
+
+
+"""SDR records
+
+These are really bare right now - look at the SDR commands to see
+how they actually get filled out.
+"""
+
+class Sdr(object):
+ """Base SDR record type for others to inherit"""
+ def __str__(self):
+ return "\n".join("%s: %r" % x for x in sorted(vars(self).iteritems()))
+
+class AnalogSdr(Sdr):
+ """An analog SDR record"""
+ pass
+
+class DiscreteSdr(Sdr):
+ """A discrete SDR record"""
+ pass
diff --git a/pyipmi/sel.py b/pyipmi/sel.py
new file mode 100644
index 0000000..0a6fa5b
--- /dev/null
+++ b/pyipmi/sel.py
@@ -0,0 +1,171 @@
+# 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 datetime import datetime
+import re
+
+from pyipmi import IpmiError
+
+TIME_FORMAT = '%m/%d/%Y %H:%M:%S'
+
+class SEL(object):
+ """A class to represent a SEL"""
+
+ def __init__(self, bmc):
+ self.bmc = bmc
+ self._info = self.bmc.sel_info()
+ self._alloc_info = self.bmc.sel_alloc_info()
+
+ @property
+ def entries(self):
+ return self.bmc.sel_info().entries
+
+ @property
+ def size(self):
+ return self._alloc_info.num_alloc_units
+
+ @property
+ def size_bytes(self):
+ return self.size * self._alloc_info.alloc_unit_size
+
+
+class SELRecord(object):
+ """A class to represent a SEL event record"""
+
+ def __init__(self, record_id=0, record_type=2, timestamp='',
+ generator_id=0, evm_rev=4, sensor_type=0,
+ sensor_number=0, event_type=0, event_direction=0,
+ event_data=(0, 0, 0)):
+ self.record_id = record_id
+ self.record_type = record_type
+ self.timestamp = timestamp
+ self.generator_id = generator_id
+ self.evm_rev = evm_rev
+ self.sensor_type = sensor_type
+ self.sensor_number = sensor_number
+ self.event_type = event_type
+ self.event_direction = event_direction
+ self.event_data = event_data
+
+ def __eq__(self, other):
+ return (self.record_type == other.record_type and
+ self.generator_id == other.generator_id and
+ self.evm_rev == other.evm_rev and
+ self.sensor_type == other.sensor_type and
+ self.sensor_number == other.sensor_number and
+ self.event_type == other.event_type and
+ self.event_direction == other.event_direction and
+ self.event_data == other.event_data)
+
+ sensor_types = {'Reserved': 0}
+ event_types = {'Unspecified': 0}
+
+ def normalize(self):
+ #TODO: replace this with something better
+ try:
+ int(self.sensor_type)
+ except ValueError:
+ self.sensor_type = self.sensor_types[self.sensor_type]
+
+ try:
+ int(self.event_type)
+ except ValueError:
+ self.event_type = self.event_types[self.event_type]
+
+
+class OEMSELRecord(object):
+ """A class to represent an OEM SEL record type C0h-DFh"""
+
+
+class TimestampedOEMSELRecord(OEMSELRecord):
+ """A class to represent an OEM SEL record type E0h-FFh"""
+
+
+class SELTimestamp(object):
+ """A class to represent a Timestamp"""
+
+ parser = re.compile('%s/%s/%s %s:%s:%s' % (
+ '(?P<mon>\d{2})', '(?P<day>\d{2})', '(?P<year>\d{4})',
+ '(?P<hour>\d{2})', '(?P<min>\d{2})', '(?P<sec>\d{2})'))
+ default_time = parser.match('01/01/1970 00:00:00')
+
+ def __init__(self, timestamp=''):
+ match = self.parser.match(timestamp)
+ if match is None:
+ match = self.default_time
+
+ mdict = dict((k, int(v)) for k, v in match.groupdict().iteritems())
+ self.time = datetime(mdict['year'], mdict['mon'], mdict['day'],
+ mdict['hour'], mdict['min'], mdict['sec'])
+
+ @property
+ def timestamp(self):
+ return repr(self)
+
+ def __repr__(self):
+ return self.time.strftime(TIME_FORMAT)
+
+ def __str__(self):
+ return str(self.time)
+
+ def __eq__(self, other):
+ return self.time == other.time
+
+ def __ne__(self, other):
+ return self.time != other.time
+
+ def __lt__(self, other):
+ return self.time < other.time
+
+ def __le__(self, other):
+ return self.time <= other.time
+
+ def __gt__(self, other):
+ return self.time > other.time
+
+ def __ge__(self, other):
+ return self.time >= other.time
+
+
+class SELInfo(object):
+ """A class to represent SEL info"""
+
+
+class SELAllocInfo(object):
+ """A class to represent SEL allocation info"""
+
+
+class SELOverflowError(IpmiError):
+ """An error that is thrown when the SEL is full"""
+
+
+class SELTimestampError(IpmiError):
+ """An error thrown with invalid timestamps"""
diff --git a/pyipmi/server.py b/pyipmi/server.py
new file mode 100644
index 0000000..591b686
--- /dev/null
+++ b/pyipmi/server.py
@@ -0,0 +1,76 @@
+# 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.
+
+
+"""A representation of a server.
+
+This may go away - it doesn't do anything other than what chassis does.
+
+Don't add to this.
+"""
+
+from chassis import Chassis
+
+__all__ = ['Server']
+
+class Server:
+ """A server is managed over IPMI"""
+
+ def __init__(self, bmc):
+ """
+ Arguments:
+
+ bmc -- the BMC that's managing this server
+ """
+ self.bmc = bmc
+ self._chassis = Chassis(self.bmc.handle)
+
+ def power_off(self):
+ """Power off server if it's powered on"""
+ if self._chassis.is_powered:
+ self._chassis.power_off()
+
+ def power_on(self):
+ """Power on server if it's powered off"""
+ if not self._chassis.is_powered:
+ self._chassis.power_on()
+
+ def power_cycle(self):
+ """Power cycle server"""
+ self._chassis.power_cycle()
+
+ def hard_reset(self):
+ """Warm reset server"""
+ self._chassis.hard_reset()
+
+ @property
+ def is_powered(self):
+ """True if server is powered, otherwise false"""
+ return self._chassis.is_powered
diff --git a/pyipmi/sol.py b/pyipmi/sol.py
new file mode 100644
index 0000000..c103152
--- /dev/null
+++ b/pyipmi/sol.py
@@ -0,0 +1,228 @@
+# 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.
+
+
+"""Helper objects for various SOL-related commands"""
+
+from sys import stderr
+
+import pexpect
+from pexpect import TIMEOUT, EOF
+from pyipmi import IpmiError
+
+ESCAPE_SEQUENCES = {
+ "IpmiTool" : {
+ "terminate" : "~.",
+ "list_escapes" : "~?",
+ "escape_char" : "~~"
+ }
+}
+
+TOOL_RESPONSES = {
+ "IpmiTool" : {
+ "open" : "[SOL Session operational. Use ~? for help]",
+ "close" : "[terminated ipmitool]",
+ "send_error": "Error sending SOL data: FAIL",
+ "bmc_closed": "SOL session closed by BMC",
+ "activation_error": "Error: No response activating SOL payload",
+ "session_error": "Error: Unable to establish IPMI v2 / RMCP+ session",
+ "deactivation_error": "Error: No response de-activating SOL payload",
+ }
+}
+
+class SOLError(IpmiError):
+ """SOL error"""
+
+
+class SOLConsole(object):
+ """Create and control an SOL session"""
+
+ def __init__(self, bmc, hostname, username, password):
+ self.isopen = False
+ self._bmc = bmc
+ self._toolname = bmc.handle._tool.__class__.__name__
+ self.escapes = ESCAPE_SEQUENCES[self._toolname]
+ self.responses = TOOL_RESPONSES[self._toolname]
+
+ # save authenication info
+ self._auth_info = {
+ 'hostname' : hostname,
+ 'username' : username,
+ 'password' : password
+ }
+
+ # activate SOL session
+ self._proc = self._bmc.activate_payload()
+ self._proc.timeout = 5
+ self.expect_exact(self.responses['open'])
+
+ # set up log files
+ self._proc.logfile_read = file('sol_read.log', 'w')
+ self._proc.logfile_send = file('sol_write.log', 'w')
+
+ try:
+ self._login()
+ except IpmiError:
+ self.close()
+ raise
+
+ self.isopen = True
+
+ def __del__(self):
+ if self.isopen:
+ self.close()
+ self._bmc = None
+
+ def close(self):
+ self._logout()
+ self.send(self.escapes['terminate'])
+ try:
+ self.expect_exact(self.responses['close'], timeout=2)
+ except TIMEOUT, EOF:
+ try:
+ self._bmc.deactivate_payload()
+ except IpmiError as e:
+ assert e.message.find(self.responses['deactivation_error']) > -1
+ stderr.write(e.message)
+
+ if self._proc.isalive():
+ self._proc.close()
+
+ self.isopen = False
+
+ def _login(self):
+ hostname = self._auth_info['hostname']
+ username = self._auth_info['username']
+ password = self._auth_info['password']
+
+ self.prompt = '%s@%s.*[$#] ' % (username, hostname)
+ self.login_prompt = hostname + ' login: '
+
+ # once we've activated a session, either we're logged in,
+ # we need to log in, or we're not getting any data back
+ login_patterns = [self.login_prompt, self.prompt, TIMEOUT, EOF]
+ self.sendline()
+ index = self.expect(login_patterns)
+
+ # if we haven't found a prompt (index > 1),
+ # try sending various control characters
+ # control characters to send
+ controls = ['\\', 'c', 'd']
+ while index > 1:
+ try:
+ self.sendcontrol(controls.pop())
+ index = self.expect(login_patterns)
+ except IndexError:
+ raise IpmiError('SOL session unresponsive')
+
+ if index == 0:
+ # need to log in
+ try:
+ self.sendline(username)
+ self.expect_exact('Password: ')
+ self.sendline(password)
+ self.expect(self.prompt)
+ except TIMEOUT:
+ raise #IpmiError('%s@%s: failed login' % (username, hostname))
+ elif index == 1:
+ # we're already logged in
+ pass
+
+ # make the prompt more predictable
+ self.sendline('export PS1="\u@\h:~\$ "')
+ self.expect(self.prompt)
+
+ def _logout(self):
+ self.sendcontrol('c')
+ self.sendline('logout')
+ return self.expect([self.login_prompt, TIMEOUT, EOF]) == 0
+
+ ######################################
+ # #
+ # Wrappers for pexpect functionality #
+ # #
+ ######################################
+
+ def expect(self, pattern, timeout=-1, searchwindowsize=None):
+ return self._proc.expect(pattern, timeout, searchwindowsize)
+
+ def expect_exact(self, pattern, timeout=-1, searchwindowsize=None):
+ return self._proc.expect_exact(pattern, timeout, searchwindowsize)
+
+ def read(self, size=-1):
+ # pexpect implements this function by expecting the delimiter
+ # the default delimiter is EOF, which for our purposes, is
+ # unlikely to be reached. So instead, use TIMEOUT
+ # as the delimiter for now
+ prev_delimiter = self._proc.delimiter
+ self._proc.delimiter = TIMEOUT
+ data = self._proc.read(size)
+ self._proc.delimiter = prev_delimiter
+ return data
+
+ def readline(self, size=-1):
+ return self._proc.readline(size)
+
+ def send(self, s):
+ return self._proc.send(s)
+
+ def sendline(self, s=""):
+ return self.send(s + "\n")
+
+ def sendcontrol(self, char):
+ return self._proc.sendcontrol(char)
+
+ @property
+ def match(self):
+ return self._proc.match
+
+
+# map config params to range of possible values
+SOL_CONFIGURATION_PARAMETERS = {
+ "set_in_progress" : ["set_in_progress", "set_complete", "commit_write"],
+ "enable" : [True, False],
+ "force_encryption" : [True, False],
+ "force_authentication" : [True, False],
+ "privilege_level" : ["USER", "OPERATOR", "ADMINISTRATOR", "OEM"],
+ "character_accumulate_interval" : range(1, 256),
+ "character_send_threshold" : range(256),
+ "retry_count" : range(8),
+ "retry_interval" : range(256),
+ "volatile_bit_rate" : [9.6, 19.2, 38.4, 57.6, 115.2], #TODO: "serial"
+ "non_volatile_bit_rate" : [9.6, 19.2, 38.4, 57.6, 115.2], #TODO: "serial"
+ "payload_channel" : [], # implementation specific
+ "payload_port_number" : [], # implementation specific
+ # TODO: support OEM parameters
+}
+
+# map tools to a list of unsettable params for that tool
+TOOL_RESTRICTIONS = {
+ "IpmiTool": ["payload_channel", "payload_port_number"],
+}
diff --git a/pyipmi/tools/__init__.py b/pyipmi/tools/__init__.py
new file mode 100644
index 0000000..fcaba71
--- /dev/null
+++ b/pyipmi/tools/__init__.py
@@ -0,0 +1,35 @@
+# 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.
+
+
+"""Implementations of Tool"""
+from ipmitool import IpmiTool
+from ipmidcmi import IpmiDcmi
+from ipmi_pef_config import IpmiPEFConfig
diff --git a/pyipmi/tools/ipmi_pef_config.py b/pyipmi/tools/ipmi_pef_config.py
new file mode 100644
index 0000000..4923f2c
--- /dev/null
+++ b/pyipmi/tools/ipmi_pef_config.py
@@ -0,0 +1,126 @@
+# 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.
+
+
+"""An implementation of Tool for ipmi-pef-config (module of FreeIPMI) support"""
+
+import subprocess, sys, pexpect
+from pyipmi import Tool, IpmiError, InteractiveCommand
+
+class IpmiPEFConfig(Tool):
+ """Implements interaction with ipmi-pef-config
+
+ Currently only supports one off commands, persistent sessions will come.
+ """
+ def __init__(self, *args, **kwargs):
+ super(IpmiPEFConfig, self).__init__(*args, **kwargs)
+ self._ipmi_pef_config_path = self._find_ipmi_pef_config_path()
+
+ def _find_ipmi_pef_config_path(self):
+ """Get the path to the ipmi-pef-config bin.
+
+ freeipmi puts all of its binaries in /usr/sbin, which lots of
+ things don't have in their default path. We hardcode the path to
+ it here, but only if it can't be found in $PATH (via bash's which)."""
+
+ try:
+ found = subprocess.check_output(['which', 'ipmi-pef-config']).strip()
+ except subprocess.CalledProcessError:
+ return '/usr/sbin/ipmi-pef-config'
+
+ return found
+
+ def run(self, command):
+ """Run a command via ipmi-pef-config"""
+ ipmi_args = self._ipmi_args(command)
+
+ arg_str = 'Running %s' % ' '.join(ipmi_args)
+ self._log(arg_str)
+ print arg_str
+
+ if isinstance(command, InteractiveCommand):
+ command = ipmi_args[0]
+ args = ipmi_args[1:]
+ proc = self._start_command(command, args)
+ return proc
+
+ out, err = self._execute(command, ipmi_args)
+ return command.parse_results(out, err)
+
+ def _ipmi_args(self, command):
+ """Return the command line arguments to ipmi-pef-config for command"""
+ args = [self._ipmi_pef_config_path]
+ args.extend(self._config_args)
+ args.extend(command.ipmi_pef_config_args)
+ return map(str, args)
+
+ @property
+ def _config_args(self):
+ """Return the config dependent command line arguments
+
+ This arguments are generated from the BMC's config, not from
+ a specific command. They will be the same from command to
+ command.
+ """
+ params_to_args = {
+ 'hostname' : '-h',
+ 'password' : '-p',
+ 'username' : '-u',
+ 'authtype' : '-a',
+ 'level' : '-l'
+ }
+
+ base = []
+ bmc_params = self._handle.bmc.params
+
+ for param, val in bmc_params.iteritems():
+ arg = params_to_args.get(param)
+ if arg and val:
+ base.extend([arg, str(val)])
+
+ return base
+
+ def _execute(self, command, args):
+ """Execute an ipmi-pef-config command"""
+ proc = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = proc.communicate()
+ self._log(out)
+ self._log(err)
+ sys.stdout.write(out)
+ sys.stderr.write(err)
+ if proc.returncode != 0:
+ command.handle_command_error(out, err)
+ return out, err
+
+ def _start_command(self, command, args, timeout=5):
+ return pexpect.spawn(command, args, timeout=timeout,
+ logfile=self._handle._log_file)
diff --git a/pyipmi/tools/ipmi_pet.py b/pyipmi/tools/ipmi_pet.py
new file mode 100644
index 0000000..05ce17e
--- /dev/null
+++ b/pyipmi/tools/ipmi_pet.py
@@ -0,0 +1,126 @@
+# 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.
+
+
+"""An implementation of Tool for ipmi-pet (module of FreeIPMI) support"""
+
+import subprocess, sys, pexpect
+from pyipmi import Tool, IpmiError, InteractiveCommand
+
+class IpmiPET(Tool):
+ """Implements interaction with ipmi-pet
+
+ Currently only supports one off commands, persistent sessions will come.
+ """
+ def __init__(self, *args, **kwargs):
+ super(IpmiPET, self).__init__(*args, **kwargs)
+ self._ipmi_pet_path = self._find_ipmi_pet_path()
+
+ def _find_ipmi_pet_path(self):
+ """Get the path to the ipmi-pet bin.
+
+ freeipmi puts all of its binaries in /usr/sbin, which lots of
+ things don't have in their default path. We hardcode the path to
+ it here, but only if it can't be found in $PATH (via bash's which)."""
+
+ try:
+ found = subprocess.check_output(['which', 'ipmi-pet']).strip()
+ except subprocess.CalledProcessError:
+ return '/usr/sbin/ipmi-pet'
+
+ return found
+
+ def run(self, command):
+ """Run a command via ipmi-pet"""
+ ipmi_args = self._ipmi_args(command)
+
+ arg_str = 'Running %s' % ' '.join(ipmi_args)
+ self._log(arg_str)
+ print arg_str
+
+ if isinstance(command, InteractiveCommand):
+ command = ipmi_args[0]
+ args = ipmi_args[1:]
+ proc = self._start_command(command, args)
+ return proc
+
+ out, err = self._execute(command, ipmi_args)
+ return command.parse_results(out, err)
+
+ def _ipmi_args(self, command):
+ """Return the command line arguments to ipmi-pet for command"""
+ args = [self._ipmi_pet_path]
+ args.extend(self._config_args)
+ args.extend(command.ipmi_pet_args)
+ return map(str, args)
+
+ @property
+ def _config_args(self):
+ """Return the config dependent command line arguments
+
+ This arguments are generated from the BMC's config, not from
+ a specific command. They will be the same from command to
+ command.
+ """
+ params_to_args = {
+ 'hostname' : '-h',
+ 'password' : '-p',
+ 'username' : '-u',
+ 'authtype' : '-a',
+ 'level' : '-l'
+ }
+
+ base = []
+ bmc_params = self._handle.bmc.params
+
+ for param, val in bmc_params.iteritems():
+ arg = params_to_args.get(param)
+ if arg and val:
+ base.extend([arg, str(val)])
+
+ return base
+
+ def _execute(self, command, args):
+ """Execute an ipmi-pet command"""
+ proc = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = proc.communicate()
+ self._log(out)
+ self._log(err)
+ sys.stdout.write(out)
+ sys.stderr.write(err)
+ if proc.returncode != 0:
+ command.handle_command_error(out, err)
+ return out, err
+
+ def _start_command(self, command, args, timeout=5):
+ return pexpect.spawn(command, args, timeout=timeout,
+ logfile=self._handle._log_file)
diff --git a/pyipmi/tools/ipmidcmi.py b/pyipmi/tools/ipmidcmi.py
new file mode 100644
index 0000000..a659b60
--- /dev/null
+++ b/pyipmi/tools/ipmidcmi.py
@@ -0,0 +1,126 @@
+# 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.
+
+
+"""An implementation of Tool for ipmi-dcmi (module of FreeIPMI) support"""
+
+import subprocess, sys, pexpect
+from pyipmi import Tool, IpmiError, InteractiveCommand
+
+class IpmiDcmi(Tool):
+ """Implements interaction with ipmi-dcmi
+
+ Currently only supports one off commands, persistent sessions will come.
+ """
+ def __init__(self, *args, **kwargs):
+ super(IpmiDcmi, self).__init__(*args, **kwargs)
+ self._ipmidcmi_path = self._find_ipmidcmi_path()
+
+ def _find_ipmidcmi_path(self):
+ """Get the path to the ipmi-dcmi bin.
+
+ freeipmi puts all of its binaries in /usr/sbin, which lots of
+ things don't have in their default path. We hardcode the path to
+ it here, but only if it can't be found in $PATH (via bash's which)."""
+
+ try:
+ found = subprocess.check_output(['which', 'ipmi-dcmi']).strip()
+ except subprocess.CalledProcessError:
+ return '/usr/sbin/ipmi-dcmi'
+
+ return found
+
+ def run(self, command):
+ """Run a command via ipmi-dcmi"""
+ ipmi_args = self._ipmi_args(command)
+
+ arg_str = 'Running %s' % ' '.join(ipmi_args)
+ self._log(arg_str)
+ print arg_str
+
+ if isinstance(command, InteractiveCommand):
+ command = ipmi_args[0]
+ args = ipmi_args[1:]
+ proc = self._start_command(command, args)
+ return proc
+
+ out, err = self._execute(command, ipmi_args)
+ return command.parse_results(out, err)
+
+ def _ipmi_args(self, command):
+ """Return the command line arguments to ipmi-dcmi for command"""
+ args = [self._ipmidcmi_path]
+ args.extend(self._config_args)
+ args.extend(command.ipmidcmi_args)
+ return map(str, args)
+
+ @property
+ def _config_args(self):
+ """Return the config dependent command line arguments
+
+ This arguments are generated from the BMC's config, not from
+ a specific command. They will be the same from command to
+ command.
+ """
+ params_to_args = {
+ 'hostname' : '-h',
+ 'password' : '-p',
+ 'username' : '-u',
+ 'authtype' : '-a',
+ 'level' : '-l'
+ }
+
+ base = []
+ bmc_params = self._handle.bmc.params
+
+ for param, val in bmc_params.iteritems():
+ arg = params_to_args.get(param)
+ if arg and val:
+ base.extend([arg, str(val)])
+
+ return base
+
+ def _execute(self, command, args):
+ """Execute an ipmi-dcmi command"""
+ proc = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = proc.communicate()
+ self._log(out)
+ self._log(err)
+ sys.stdout.write(out)
+ sys.stderr.write(err)
+ if proc.returncode != 0:
+ command.handle_command_error(out, err)
+ return out, err
+
+ def _start_command(self, command, args, timeout=5):
+ return pexpect.spawn(command, args, timeout=timeout,
+ logfile=self._handle._log_file)
diff --git a/pyipmi/tools/ipmitool.py b/pyipmi/tools/ipmitool.py
new file mode 100644
index 0000000..75cb179
--- /dev/null
+++ b/pyipmi/tools/ipmitool.py
@@ -0,0 +1,111 @@
+# 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.
+
+
+"""An implementation of Tool for ipmitool support"""
+
+import os, subprocess, pexpect
+from pyipmi import Tool, InteractiveCommand
+
+class IpmiTool(Tool):
+ """Implements interaction with impitool
+
+ Currently only supports one off commands, persistent sessions will come.
+ """
+
+ def run(self, command):
+ """Run a command via ipmitool"""
+ ipmi_args = self._ipmi_args(command)
+
+ arg_str = 'Running %s' % ' '.join(ipmi_args)
+ self._log(arg_str)
+
+ if isinstance(command, InteractiveCommand):
+ command = ipmi_args[0]
+ args = ipmi_args[1:]
+ proc = self._start_command(command, args)
+ return proc
+
+ out, err = self._execute(command, ipmi_args)
+ return command.parse_results(out, err)
+
+ def _ipmi_args(self, command):
+ """Return the command line arguments to ipmitool for command"""
+ if 'IPMITOOL_PATH' in os.environ:
+ args = [os.environ['IPMITOOL_PATH']]
+ else:
+ args = ['ipmitool']
+ args.extend(self._config_args)
+ args.extend(command.ipmitool_args)
+ return map(str, args)
+
+ @property
+ def _config_args(self):
+ """Return the config dependent command line arguments
+
+ This arguments are generated from the BMC's config, not from
+ a specific command. They will be the same from command to
+ command.
+ """
+ params_to_args = {
+ 'hostname' : '-H',
+ 'password' : '-P',
+ 'username' : '-U',
+ 'authtype' : '-A',
+ 'level' : '-L',
+ 'port' : '-p',
+ 'interface' : '-I'
+ }
+
+ base = []
+ bmc_params = self._handle.bmc.params
+
+ for param, val in bmc_params.iteritems():
+ arg = params_to_args.get(param)
+ if arg and val:
+ base.extend([arg, str(val)])
+
+ return base
+
+ def _execute(self, command, args):
+ """Execute an ipmitool command"""
+ proc = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = proc.communicate()
+ self._log(out)
+ self._log(err)
+ if proc.returncode != 0:
+ command.handle_command_error(out, err)
+ return out, err
+
+ def _start_command(self, command, args, timeout=5):
+ return pexpect.spawn(command, args, timeout=timeout,
+ logfile=self._handle._log_file)
diff --git a/pyipmi/tools/responseparser.py b/pyipmi/tools/responseparser.py
new file mode 100644
index 0000000..abbf785
--- /dev/null
+++ b/pyipmi/tools/responseparser.py
@@ -0,0 +1,269 @@
+# 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.
+
+
+"""Tool-independent mix-in for parsing IPMI results"""
+
+from pyipmi import IpmiError
+import string
+import inspect
+
+
+def str_to_list(val, **params):
+ """convert string to list of substrings (default: single words)"""
+ val = val.strip()
+ if val == '':
+ return []
+
+ delimiter = params.get('delimiter', " ")
+ return map(string.strip, val.split(delimiter))
+
+
+def str2bool(val):
+ """True if val is 'true', 'yes' or 'enabled, otherwise false"""
+ return val.lower() in ['true', 'yes', 'enabled']
+
+
+def str_to_dict(val, **params):
+ """Returns the contents of the string 'val' as a dictionary"""
+ result = {}
+ operator = params.get('operator', ':')
+ delimiter = params.get('delimiter', '\n')
+ value_parser = params.get('value_parser', str)
+
+ params['operator'] = params.get('value_operator', None)
+ params['delimiter'] = params.get('value_delimiter', None)
+
+ entries = val.split(delimiter)
+ for entry in entries:
+ key, op, value = entry.partition(operator)
+ result[field_to_attr(key.strip())] = value_parser(value)
+ return result
+
+
+def paren_pair(val):
+ """Convert 'foo (bar)' to ['foo', 'bar']"""
+ return [p.strip(' )') for p in val.split('(')]
+
+
+def field_to_attr(field_name):
+ """Convert a field name to an attribute name
+
+ Make the field all lowercase and replace ' ' with '_'
+ (replace space with underscore)
+ """
+ result = field_name.lower()
+ if result[0:1].isdigit():
+ result = "n_" + result
+ result = result.replace(' ', '_')
+ result = result.replace('/', '_')
+ result = result.replace('-', '_')
+ result = result.replace('.', '_')
+ result = result.replace('+', '_plus')
+ return result
+
+
+class ResponseParserMixIn(object):
+ """Add this MixIn to a Command to enable it to parse response strings into
+ response data structures"""
+
+ """
+ Supplied parse methods are parse_colon_record() (the default) and
+ parse_colon_record_list(). Override the default in a derived class
+ by setting the "response_parser" field to the name of the desired
+ method.
+ """
+
+ def parse_colon_record(self, response, err):
+ """Parse records of key : value separated lines
+
+ This expects response to be a string of newline separated
+ field/value pairs, with each field/value being separated by a
+ colon and optional whitespace.
+
+ Records this parses look like this:
+
+ Sensor Data Type : Blah
+ Somefield : Somevalue
+
+ The type of the result returned and the conversion of key/values
+ in the text result to attribute names/values in the returned object
+ are determined by calling get_response_types on this command instance,
+ which gives a way for the result type and mapping to change based
+ on the contents of the response.
+ """
+ result_type, mapping = self.get_response_types(response)
+
+ if result_type == None:
+ return None
+
+ obj = result_type()
+ line, sep, rest = response.partition('\n')
+ left_over = []
+ while line != '':
+ colon_index = 10000000
+ if line.find(':') != -1:
+ colon_index = line.index(':')
+ equal_index = 10000000
+ if line.find('=') != -1:
+ equal_index = line.index('=')
+ if colon_index == 10000000 and equal_index == 10000000:
+ line, sep, rest = rest.partition('\n')
+ continue
+
+ field_seperator = min([colon_index, equal_index])
+ field = line[0:field_seperator].strip()
+ value = line[field_seperator + 1:].strip()
+
+ field_info = mapping.get(field)
+
+ if field_info == None:
+ left_over.append((field, value))
+ line, sep, rest = rest.partition('\n')
+ continue
+
+ lines_to_get = field_info.get('lines', 1) - 1
+ while lines_to_get > 0:
+ line, sep, rest = rest.partition('\n')
+ value += '\n' + line
+ lines_to_get -= 1
+
+ self.field_to_objval(obj, field_info, field, value)
+ line, sep, rest = rest.partition('\n')
+ return obj
+
+
+ def parse_colon_record_list(self, response, err):
+ """Parse multiple groups of colon records
+
+ Like colon records, but with multiple groups, each separated
+ by a blank line (two consecutive newline characters).
+
+ This returns a list of result objects rather than a single
+ result object. The type of each result object can vary based
+ on its contents, so the list isn't always of the same type
+ of objects.
+ """
+ results = []
+ records = response.split('\n\n')
+ for record in records:
+ obj = self.parse_colon_record(record.strip(), err)
+
+ if obj == None:
+ continue
+
+ results.append(obj)
+
+ return results
+
+
+ def parse_single_line(self, response, err):
+ obj = self.result_type()
+ attr_name = self.response_fields['attr']
+ setattr(obj, attr_name, response.strip())
+ return obj
+
+
+ def field_to_objval(self, obj, field_info, field_name, value):
+ """Assign a field's value to an attribute of obj
+
+ Arguments:
+ obj -- the object to set the attribute on. this is some record type
+ object - the exact varies depending on the command being
+ executed.
+ field_info -- a dict describing the field. See "Field Info" below for
+ more info.
+ field_name -- the name of the field as given in the IPMI results.
+ this will be used as the name of the attribute unless a
+ 'attr' key/value is given in the field_info dict.
+ value -- the value of the field as given in the IPMI results. This
+ value will be assigned to the attribute unless a 'parser'
+ key/value is specified in the field_info dict
+
+ Field Info:
+ If an 'attr' key/value is present, the value will be used for the
+ attribute name of this field instead of 'field_name'.
+
+ If a 'parser' key/value is present, the value will be passed to
+ it, and the result will be assigned to the attribute. The default
+ parser is str().
+ """
+ str_func = lambda x: str(x)
+ attr_name = field_info.get('attr', field_to_attr(field_name))
+ attr_parser = supplied_parser = field_info.get('parser', str_func)
+
+ args, varargs, keywords, defaults = inspect.getargspec(attr_parser)
+ if keywords == None:
+ attr_parser = lambda x, **y: supplied_parser(x)
+ setattr(obj, attr_name, attr_parser(value, **field_info))
+
+ def get_response_types(self, response):
+ """Return the result type and field mappings
+
+ The result type is the class of the result to be used. The field
+ mappings are given in self.response_fields, and are a
+ dict mapping field names to field info dicts. See 'field info' in
+ the doc for field_to_objval above.
+
+ Arguments:
+ response -- the text of the command response. It's not used in
+ this base method, but might be used in a subclass's version of
+ this method to allow different result types and mappings to be
+ used based on the contents of the response.
+ """
+ return self.result_type, self.response_fields
+
+ def parse_response(self, out, err):
+ """Parse the response to a command
+
+ Arguments:
+ out -- the text response of an IPMI command from stdout
+ err -- the text response of an IPMI command from stderr
+ """
+ return self.response_parser(out, err)
+
+ def parse_results(self, out, err):
+ """Parse the results if a result type is specified
+
+ If there is not 'result_type' attribute for this this command, return
+ None.
+ """
+ try:
+ result_type = self.result_type
+ except AttributeError:
+ return None
+
+ return self.parse_response(out, err)
+
+ def handle_command_error(self, out, err):
+ """Handle an error from running the command"""
+ raise IpmiError(err.strip())
+
+ response_parser = parse_colon_record
diff --git a/pyipmi/user.py b/pyipmi/user.py
new file mode 100644
index 0000000..d69f00e
--- /dev/null
+++ b/pyipmi/user.py
@@ -0,0 +1,57 @@
+# 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.
+
+
+"""ipmitool user Results"""
+
+class UserListResults(object):
+ """Object to hold user list [channel] results"""
+ pass
+
+
+class UserSetNameResults(object):
+ """Object to hold user set name results"""
+ pass
+
+class UserSetPasswordResults(object):
+ """Object to hold user set name results"""
+ pass
+
+class UserEnableResults(object):
+ """Object to hold user set name results"""
+ pass
+
+class UserDisableResults(object):
+ """Object to hold user set name results"""
+ pass
+
+class UserPrivResults(object):
+ """Object to hold user set name results"""
+ pass
diff --git a/pyipmi/watchdog.py b/pyipmi/watchdog.py
new file mode 100644
index 0000000..91e2248
--- /dev/null
+++ b/pyipmi/watchdog.py
@@ -0,0 +1,46 @@
+# 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.
+
+
+"""ipmitool mc watchdog Results"""
+
+class WatchdogGetResult(object):
+ """Object to hold watchdog get results"""
+ pass
+
+
+class WatchdogResetResult(object):
+ """Object to hold watchdog reset results"""
+ pass
+
+
+class WatchdogOffResult(object):
+ """Object to hold watchdog off results"""
+ pass
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..57ed35a
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,45 @@
+# 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 setuptools import setup
+
+setup(
+ name='pyipmi',
+ version='0.7.1',
+ packages=['pyipmi',
+ 'pyipmi.commands',
+ 'pyipmi.tools'],
+ description='Wrapper for IPMI clients',
+ author='Calxeda',
+ install_requires=['pexpect'],
+ classifiers=[
+ 'License :: OSI Approved :: BSD License',
+ 'Programming Language :: Python :: 2.7']
+)