From 2e54d50d349aafdc189eba2702e5913835cdf88f Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Thu, 9 May 2013 15:12:28 +0000 Subject: Imported from /home/lorry/working-area/delta_pyipmi-tarball/pyipmi-0.7.1.tar.gz. --- PKG-INFO | 12 + README | 5 + pyipmi.egg-info/PKG-INFO | 12 + pyipmi.egg-info/SOURCES.txt | 62 ++++ pyipmi.egg-info/dependency_links.txt | 1 + pyipmi.egg-info/requires.txt | 1 + pyipmi.egg-info/top_level.txt | 1 + pyipmi/__init__.py | 213 +++++++++++++ pyipmi/bmc.py | 567 +++++++++++++++++++++++++++++++++++ pyipmi/bootdev.py | 40 +++ pyipmi/bootparam.py | 35 +++ pyipmi/channel.py | 51 ++++ pyipmi/chassis.py | 118 ++++++++ pyipmi/commands/__init__.py | 89 ++++++ pyipmi/commands/bmc.py | 152 ++++++++++ pyipmi/commands/bootdev.py | 80 +++++ pyipmi/commands/bootparam.py | 66 ++++ pyipmi/commands/channel.py | 201 +++++++++++++ pyipmi/commands/chassis.py | 80 +++++ pyipmi/commands/data.py | 125 ++++++++ pyipmi/commands/dcmi.py | 337 +++++++++++++++++++++ pyipmi/commands/event.py | 79 +++++ pyipmi/commands/fabric.py | 306 +++++++++++++++++++ pyipmi/commands/fabric_config.py | 288 ++++++++++++++++++ pyipmi/commands/freeipmi_pef.py | 211 +++++++++++++ pyipmi/commands/fru.py | 132 ++++++++ pyipmi/commands/fw.py | 400 ++++++++++++++++++++++++ pyipmi/commands/info.py | 92 ++++++ pyipmi/commands/lan.py | 127 ++++++++ pyipmi/commands/mc.py | 69 +++++ pyipmi/commands/payload.py | 65 ++++ pyipmi/commands/pef.py | 130 ++++++++ pyipmi/commands/pet.py | 55 ++++ pyipmi/commands/sdr.py | 110 +++++++ pyipmi/commands/sel.py | 229 ++++++++++++++ pyipmi/commands/sol.py | 207 +++++++++++++ pyipmi/commands/user.py | 163 ++++++++++ pyipmi/commands/watchdog.py | 92 ++++++ pyipmi/data.py | 48 +++ pyipmi/dcmi.py | 92 ++++++ pyipmi/event.py | 41 +++ pyipmi/fabric.py | 63 ++++ pyipmi/freeipmi_pef.py | 56 ++++ pyipmi/fru.py | 56 ++++ pyipmi/fw.py | 115 +++++++ pyipmi/info.py | 41 +++ pyipmi/lan.py | 41 +++ pyipmi/mc.py | 36 +++ pyipmi/pef.py | 52 ++++ pyipmi/pet.py | 36 +++ pyipmi/sdr.py | 49 +++ pyipmi/sel.py | 171 +++++++++++ pyipmi/server.py | 76 +++++ pyipmi/sol.py | 228 ++++++++++++++ pyipmi/tools/__init__.py | 35 +++ pyipmi/tools/ipmi_pef_config.py | 126 ++++++++ pyipmi/tools/ipmi_pet.py | 126 ++++++++ pyipmi/tools/ipmidcmi.py | 126 ++++++++ pyipmi/tools/ipmitool.py | 111 +++++++ pyipmi/tools/responseparser.py | 269 +++++++++++++++++ pyipmi/user.py | 57 ++++ pyipmi/watchdog.py | 46 +++ setup.cfg | 5 + setup.py | 45 +++ 64 files changed, 7150 insertions(+) create mode 100644 PKG-INFO create mode 100644 README create mode 100644 pyipmi.egg-info/PKG-INFO create mode 100644 pyipmi.egg-info/SOURCES.txt create mode 100644 pyipmi.egg-info/dependency_links.txt create mode 100644 pyipmi.egg-info/requires.txt create mode 100644 pyipmi.egg-info/top_level.txt create mode 100644 pyipmi/__init__.py create mode 100644 pyipmi/bmc.py create mode 100644 pyipmi/bootdev.py create mode 100644 pyipmi/bootparam.py create mode 100644 pyipmi/channel.py create mode 100644 pyipmi/chassis.py create mode 100644 pyipmi/commands/__init__.py create mode 100644 pyipmi/commands/bmc.py create mode 100644 pyipmi/commands/bootdev.py create mode 100644 pyipmi/commands/bootparam.py create mode 100644 pyipmi/commands/channel.py create mode 100644 pyipmi/commands/chassis.py create mode 100644 pyipmi/commands/data.py create mode 100644 pyipmi/commands/dcmi.py create mode 100644 pyipmi/commands/event.py create mode 100644 pyipmi/commands/fabric.py create mode 100644 pyipmi/commands/fabric_config.py create mode 100644 pyipmi/commands/freeipmi_pef.py create mode 100644 pyipmi/commands/fru.py create mode 100644 pyipmi/commands/fw.py create mode 100644 pyipmi/commands/info.py create mode 100644 pyipmi/commands/lan.py create mode 100644 pyipmi/commands/mc.py create mode 100644 pyipmi/commands/payload.py create mode 100644 pyipmi/commands/pef.py create mode 100644 pyipmi/commands/pet.py create mode 100644 pyipmi/commands/sdr.py create mode 100644 pyipmi/commands/sel.py create mode 100644 pyipmi/commands/sol.py create mode 100644 pyipmi/commands/user.py create mode 100644 pyipmi/commands/watchdog.py create mode 100644 pyipmi/data.py create mode 100644 pyipmi/dcmi.py create mode 100644 pyipmi/event.py create mode 100644 pyipmi/fabric.py create mode 100644 pyipmi/freeipmi_pef.py create mode 100644 pyipmi/fru.py create mode 100644 pyipmi/fw.py create mode 100644 pyipmi/info.py create mode 100644 pyipmi/lan.py create mode 100644 pyipmi/mc.py create mode 100644 pyipmi/pef.py create mode 100644 pyipmi/pet.py create mode 100644 pyipmi/sdr.py create mode 100644 pyipmi/sel.py create mode 100644 pyipmi/server.py create mode 100644 pyipmi/sol.py create mode 100644 pyipmi/tools/__init__.py create mode 100644 pyipmi/tools/ipmi_pef_config.py create mode 100644 pyipmi/tools/ipmi_pet.py create mode 100644 pyipmi/tools/ipmidcmi.py create mode 100644 pyipmi/tools/ipmitool.py create mode 100644 pyipmi/tools/responseparser.py create mode 100644 pyipmi/user.py create mode 100644 pyipmi/watchdog.py create mode 100644 setup.cfg create mode 100644 setup.py 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 " 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 " 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 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[12]\.[05]) \((?P.+) 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\d{2})', '(?P\d{2})', '(?P\d{4})', + '(?P\d{2})', '(?P\d{2})', '(?P\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'] +) -- cgit v1.2.1