From ffe493df0f13f45963268d68b349c9dbf809a39a Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 24 Sep 2013 09:49:53 -0400 Subject: [PATCH] Use distinct exceptions for many cases Caller is likely going to want to catch certain conditions. For those, use more specific exception classes. Exceptions indicating either TODO or usage errors are being left as 'Exception' for the time being. Change-Id: I4d68a2dbc394b534d54586b9f770160c1409f720 --- pyghmi/exceptions.py | 30 ++++++++++++++++++++++++++ pyghmi/ipmi/command.py | 15 +++++++------ pyghmi/ipmi/console.py | 7 +++--- pyghmi/ipmi/private/session.py | 39 +++++++++++++++++++++------------- 4 files changed, 66 insertions(+), 25 deletions(-) create mode 100644 pyghmi/exceptions.py diff --git a/pyghmi/exceptions.py b/pyghmi/exceptions.py new file mode 100644 index 00000000..eb7ff4fd --- /dev/null +++ b/pyghmi/exceptions.py @@ -0,0 +1,30 @@ + +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 IBM Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# The Exceptions that Pyghmi can throw + + +class PyghmiException(Exception): + pass + + +class IpmiException(PyghmiException): + pass + + +class InvalidParameterValue(PyghmiException): + pass diff --git a/pyghmi/ipmi/command.py b/pyghmi/ipmi/command.py index eed1102b..ed0b19f0 100644 --- a/pyghmi/ipmi/command.py +++ b/pyghmi/ipmi/command.py @@ -15,6 +15,8 @@ # limitations under the License. # This represents the low layer message framing portion of IPMI +import pyghmi.exceptions as exc + from pyghmi.ipmi.private import session @@ -160,11 +162,12 @@ class Command(object): :returns: dict -- A dict describing the response retrieved """ if powerstate not in power_states: - raise Exception("Unknown power state %s requested" % powerstate) + raise exc.InvalidParameterValue( + "Unknown power state %s requested" % powerstate) self.newpowerstate = powerstate response = self.ipmi_session.raw_command(netfn=0, command=1) if 'error' in response: - raise Exception(response['error']) + raise exc.IpmiException(response['error']) self.powerstate = 'on' if (response['data'][0] & 1) else 'off' if self.powerstate == self.newpowerstate: return {'powerstate': self.powerstate} @@ -173,7 +176,7 @@ class Command(object): response = self.ipmi_session.raw_command( netfn=0, command=2, data=[power_states[self.newpowerstate]]) if 'error' in response: - raise Exception(response['error']) + raise exc.IpmiException(response['error']) self.lastresponse = {'pendingpowerstate': self.newpowerstate} waitattempts = 300 if not isinstance(wait, bool): @@ -193,7 +196,8 @@ class Command(object): currpowerstate = 'on' if (response['data'][0] & 1) else 'off' waitattempts -= 1 if currpowerstate != self.waitpowerstate: - raise Exception("System did not accomplish power state change") + raise exc.IpmiException( + "System did not accomplish power state change") return {'powerstate': currpowerstate} else: return self.lastresponse @@ -281,8 +285,7 @@ class Command(object): """ response = self.ipmi_session.raw_command(netfn=0, command=1) if 'error' in response: - raise Exception(response['error']) - return + raise exc.IpmiException(response['error']) assert(response['command'] == 1 and response['netfn'] == 1) self.powerstate = 'on' if (response['data'][0] & 1) else 'off' return {'powerstate': self.powerstate} diff --git a/pyghmi/ipmi/console.py b/pyghmi/ipmi/console.py index 2399a498..012e74b8 100644 --- a/pyghmi/ipmi/console.py +++ b/pyghmi/ipmi/console.py @@ -41,7 +41,7 @@ class Console(object): #TODO(jbjohnso): still need an exit and a data callin function def __init__(self, bmc, userid, password, - iohandler=None, port=623, + iohandler, port=623, force=False, kg=None): if type(iohandler) == tuple: # two file handles self.console_in = iohandler[0] @@ -53,8 +53,6 @@ class Console(object): self.console_out = None self.console_in = None self.out_handler = iohandler - else: - raise(Exception('No IO handler provided')) if self.console_in is not None: fcntl.fcntl(self.console_in.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) self.remseq = 0 @@ -136,7 +134,8 @@ class Console(object): #data[6:7] is the promise of how small packets are going to be, but we #don't have any reason to worry about it if (data[8] + (data[9] << 8)) != 623: - raise Exception("TODO(jbjohnso): support atypical SOL port number") + #TODO(jbjohnso): support atypical SOL port number + raise NotImplementedError("Non-standard SOL Port Number") #ignore data[10:11] for now, the vlan detail, shouldn't matter to this #code anyway... self.ipmi_session.sol_handler = self._got_sol_payload diff --git a/pyghmi/ipmi/private/session.py b/pyghmi/ipmi/private/session.py index 049fa24b..bcc9ce6e 100644 --- a/pyghmi/ipmi/private/session.py +++ b/pyghmi/ipmi/private/session.py @@ -29,6 +29,7 @@ from Crypto.Cipher import AES from Crypto.Hash import HMAC from Crypto.Hash import SHA +import pyghmi.exceptions as exc from pyghmi.ipmi.private import constants @@ -184,7 +185,7 @@ class Session: a client-provided callback. """ if 'error' in response: - raise Exception(response['error']) + raise exc.IpmiException(response['error']) def __init__(self, bmc, @@ -370,9 +371,11 @@ class Session: if (self.ipmiversion == 2.0): message.append(payload_type) if (baretype == 2): - raise Exception("TODO(jbjohnso): OEM Payloads") + #TODO(jbjohnso): OEM payload types + raise NotImplementedError("OEM Payloads") elif baretype not in constants.payload_types.values(): - raise Exception("Unrecognized payload type %d" % baretype) + raise NotImplementedError( + "Unrecognized payload type %d" % baretype) message += struct.unpack("!4B", struct.pack(" 16: - raise Exception("Username too long for IPMI, must not exceed 16") + raise exc.IpmiException( + "Username too long for IPMI, must not exceed 16") padneeded = 16 - len(self.userid) userid = self.userid + ('\x00' * padneeded) reqdata += struct.unpack("!16B", userid) @@ -1080,16 +1084,21 @@ class Session: Session.socket.sendto(self.netpacket, self.sockaddr) else: # he have not yet picked a working sockaddr for this connection, # try all the candidates that getaddrinfo provides - for res in socket.getaddrinfo(self.bmc, - self.port, - 0, - socket.SOCK_DGRAM): - sockaddr = res[4] - if (res[0] == socket.AF_INET): # convert the sockaddr AF_INET6 - newhost = '::ffff:' + sockaddr[0] - sockaddr = (newhost, sockaddr[1], 0, 0) - Session.bmc_handlers[sockaddr] = self - Session.socket.sendto(self.netpacket, sockaddr) + try: + for res in socket.getaddrinfo(self.bmc, + self.port, + 0, + socket.SOCK_DGRAM): + sockaddr = res[4] + if (res[0] == socket.AF_INET): # convert the sockaddr + # to AF_INET6 + newhost = '::ffff:' + sockaddr[0] + sockaddr = (newhost, sockaddr[1], 0, 0) + Session.bmc_handlers[sockaddr] = self + Session.socket.sendto(self.netpacket, sockaddr) + except socket.gaierror: + raise exc.IpmiException( + "Unable to transmit to specified address") def logout(self, callback=None, callback_args=None): if not self.logged: