1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
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"""
|