Improve error management
- Better handle connection errors. - Better handle login errors. - Improve exception process. - Update examples. - Make examples compatible with new configuration file. - Show exception handling. - Validate trusted SSL connection. - Various PEP8 corrections.
This commit is contained in:
parent
4ef8d92da7
commit
466534359c
@ -6,7 +6,7 @@ import os
|
||||
import sys
|
||||
import json
|
||||
import redfish
|
||||
from time import sleep
|
||||
|
||||
|
||||
# Get $HOME environment.
|
||||
HOME = os.getenv('HOME')
|
||||
@ -24,17 +24,26 @@ except IOError as e:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
|
||||
URL = config["Nodes"]["default"]["url"]
|
||||
USER_NAME = config["Nodes"]["default"]["login"]
|
||||
PASSWORD = config["Nodes"]["default"]["password"]
|
||||
URL = config["Managers"]["default"]["url"]
|
||||
USER_NAME = config["Managers"]["default"]["login"]
|
||||
PASSWORD = config["Managers"]["default"]["password"]
|
||||
|
||||
''' remote_mgmt is a redfish.RedfishConnection object '''
|
||||
remote_mgmt = redfish.connect(URL, USER_NAME, PASSWORD, verify_cert=False)
|
||||
try:
|
||||
remote_mgmt = redfish.connect(URL,
|
||||
USER_NAME,
|
||||
PASSWORD,
|
||||
simulator=False,
|
||||
verify_cert=False)
|
||||
except redfish.exception.RedfishException as e:
|
||||
sys.stderr.write(str(e.message))
|
||||
sys.stderr.write(str(e.advices))
|
||||
sys.exit(1)
|
||||
|
||||
print ("Redfish API version : %s \n" % remote_mgmt.get_api_version())
|
||||
|
||||
# Uncomment following line to reset the blade !!!
|
||||
#remote_mgmt.Systems.systems_list[0].reset_system()
|
||||
# remote_mgmt.Systems.systems_list[0].reset_system()
|
||||
|
||||
# TODO : create an attribute to link the managed system directly
|
||||
# and avoid systems_list[0]
|
||||
|
@ -23,13 +23,22 @@ except IOError as e:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
|
||||
URL = config["Nodes"]["default"]["url"]
|
||||
USER_NAME = config["Nodes"]["default"]["login"]
|
||||
PASSWORD = config["Nodes"]["default"]["password"]
|
||||
URL = config["Managers"]["default"]["url"]
|
||||
USER_NAME = config["Managers"]["default"]["login"]
|
||||
PASSWORD = config["Managers"]["default"]["password"]
|
||||
|
||||
''' remoteMgmt is a redfish.RedfishConnection object '''
|
||||
remote_mgmt = redfish.connect(URL, USER_NAME, PASSWORD,
|
||||
simulator=True, enforceSSL=False)
|
||||
try:
|
||||
remote_mgmt = redfish.connect(URL,
|
||||
USER_NAME,
|
||||
PASSWORD,
|
||||
simulator=True,
|
||||
enforceSSL=False)
|
||||
except redfish.exception.RedfishException as e:
|
||||
sys.stderr.write(e.message)
|
||||
sys.stderr.write(e.advices)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
print("Redfish API version : {} \n".format(remote_mgmt.get_api_version()))
|
||||
print("UUID : {} \n".format(remote_mgmt.Root.get_api_UUID()))
|
||||
|
@ -1,21 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
import config
|
||||
|
||||
|
||||
class RedfishException(Exception):
|
||||
"""Base class for redfish exceptions"""
|
||||
def __init__(self, message=None, **kwargs):
|
||||
def __init__(self, message, **kwargs):
|
||||
self.kwargs = kwargs
|
||||
self.message = message
|
||||
self.advices = None
|
||||
config.logger.error(message)
|
||||
|
||||
|
||||
class ConnectionFailureException(RedfishException):
|
||||
def __init__(self, message, **kwargs):
|
||||
super(ConnectionFailureException, self).__init__(message, **kwargs)
|
||||
self.advices = '1- Check if the url is the correct one\n' + \
|
||||
'2- Check if your device is answering on the network\n'
|
||||
|
||||
|
||||
class InvalidRedfishContentException(RedfishException):
|
||||
def __init__(self, message, **kwargs):
|
||||
super(InvalidRedfishContentException, self).__init__(message, **kwargs)
|
||||
self.advices = \
|
||||
'1- Check if the url is the correct one\n' + \
|
||||
' Most of the time you are not pointing to the rest API\n'
|
||||
|
||||
|
||||
class NonTrustedCertificatException(RedfishException):
|
||||
def __init__(self, message, **kwargs):
|
||||
super(NonTrustedCertificatException, self).__init__(message, **kwargs)
|
||||
self.advices = \
|
||||
'1- Check if the url is the correct one\n' + \
|
||||
'2- Check if your device has a valid trusted certificat\n' + \
|
||||
' You can use openssl to validate it using the command :\n' + \
|
||||
' openssl s_client -showcerts -connect <server>:443\n'
|
||||
|
||||
|
||||
class AuthenticationFailureException(RedfishException):
|
||||
def __init__(self, message=None, **kwargs):
|
||||
super(AuthenticationFailureException, self).__init__(message=None, **kwargs)
|
||||
config.logger.error(message)
|
||||
# TODO
|
||||
# Give a bit more details about the failure (check login etc...)
|
||||
sys.exit(1)
|
||||
|
||||
def __init__(self, message, **kwargs):
|
||||
super(AuthenticationFailureException, self).__init__(message, **kwargs)
|
||||
self.message += str(kwargs['code'])
|
||||
self.queryAnswer = kwargs['queryAnswer']
|
||||
if kwargs['code'] == 400:
|
||||
self.message += ': ' + self.queryAnswer['Messages'][0]['MessageID']
|
||||
self.advices = '1- Check your credentials\n'
|
||||
self.message += '\n'
|
||||
|
||||
|
||||
class LogoutFailureException(RedfishException):
|
||||
pass
|
||||
|
@ -271,7 +271,7 @@ class RedfishConnection(object):
|
||||
#
|
||||
# print self.systemCollection.Name
|
||||
#
|
||||
# ========================================================================
|
||||
# ========================================================================
|
||||
def get_api_version(self):
|
||||
"""Return api version.
|
||||
|
||||
@ -286,14 +286,15 @@ class RedfishConnection(object):
|
||||
url = self.Root.get_link_url(
|
||||
mapping.redfish_mapper.map_sessionservice()
|
||||
)
|
||||
|
||||
# Handle login with redfish 1.00, url must be :
|
||||
|
||||
# Handle login with redfish 1.00, url must be :
|
||||
# /rest/v1/SessionService/Sessions as specified by the specification
|
||||
if float(mapping.redfish_version) >= 1.00:
|
||||
url += '/Sessions'
|
||||
|
||||
# Craft request body and header
|
||||
requestBody = {"UserName": self.connection_parameters.user_name , "Password": self.connection_parameters.password}
|
||||
config.logger.debug(requestBody)
|
||||
header = {'Content-type': 'application/json'}
|
||||
# =======================================================================
|
||||
# Tortilla seems not able to provide the header of a post request answer.
|
||||
@ -308,13 +309,16 @@ class RedfishConnection(object):
|
||||
headers=header,
|
||||
verify=self.connection_parameters.verify_cert
|
||||
)
|
||||
|
||||
|
||||
# =======================================================================
|
||||
# TODO : Manage exception with a class.
|
||||
# =======================================================================
|
||||
if auth.status_code != 201:
|
||||
raise exception.AuthenticationFailureException("Login request return an invalid status code")
|
||||
#sysraise "Error getting token", auth.status_code
|
||||
try:
|
||||
answer=auth.json()
|
||||
except ValueError as e:
|
||||
answer = ""
|
||||
raise exception.AuthenticationFailureException("Login request return an invalid status code ", code=auth.status_code, queryAnswer=answer)
|
||||
|
||||
self.connection_parameters.auth_token = auth.headers.get("x-auth-token")
|
||||
self.connection_parameters.user_uri = auth.headers.get("location")
|
||||
|
@ -1,12 +1,14 @@
|
||||
# coding=utf-8
|
||||
|
||||
import pprint
|
||||
import re
|
||||
from urlparse import urljoin
|
||||
import requests
|
||||
import simplejson
|
||||
import tortilla
|
||||
import config
|
||||
import mapping
|
||||
import re
|
||||
import exception
|
||||
|
||||
# Global variable
|
||||
|
||||
@ -28,10 +30,25 @@ class Base(object):
|
||||
headers={'x-auth-token': connection_parameters.auth_token}
|
||||
)
|
||||
except requests.ConnectionError as e:
|
||||
print e
|
||||
# Log and transmit the exception.
|
||||
config.logger.error("Connection error : %s", e)
|
||||
raise e
|
||||
config.logger.info("Raise a RedfishException to upper level")
|
||||
msg = "Connection error : {}\n".format(e.message)
|
||||
raise exception.ConnectionFailureException(msg)
|
||||
except simplejson.scanner.JSONDecodeError as e:
|
||||
# Log and transmit the exception.
|
||||
config.logger.info("Raise a RedfishException to upper level")
|
||||
msg = \
|
||||
"Ivalid content : Content does not appear to be a valid " + \
|
||||
"Redfish json\n"
|
||||
raise exception.InvalidRedfishContentException(msg)
|
||||
except TypeError as e:
|
||||
# This happen connecting to a manager using non trusted
|
||||
# SSL certificats.
|
||||
# The exception is not what could be expected in such case but this
|
||||
# is the one provided by Tortilla.
|
||||
config.logger.info("Raise a RedfishException to upper level")
|
||||
msg = "Connection error\n"
|
||||
raise exception.NonTrustedCertificatException(msg)
|
||||
print self.data
|
||||
|
||||
def get_link_url(self, link_type):
|
||||
@ -43,7 +60,7 @@ class Base(object):
|
||||
|
||||
"""
|
||||
self.links=[]
|
||||
|
||||
|
||||
# Manage standard < 1.0
|
||||
if float(mapping.redfish_version) < 1.00:
|
||||
links = getattr(self.data, mapping.redfish_mapper.map_links())
|
||||
@ -53,7 +70,7 @@ class Base(object):
|
||||
links = getattr(self.data, link_type)
|
||||
link = getattr(links, mapping.redfish_mapper.map_links_ref())
|
||||
return urljoin(self.url, link)
|
||||
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self.__url
|
||||
@ -61,37 +78,37 @@ class Base(object):
|
||||
@url.setter
|
||||
def url(self, url):
|
||||
self.__url = url
|
||||
|
||||
|
||||
def get_parameter(self, parameter_name):
|
||||
"""Generic function to get any system parameter
|
||||
|
||||
:param parameter_name: name of the parameter
|
||||
:returns: string -- parameter value
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
return self.data[parameter_name]
|
||||
except:
|
||||
return "Parameter does not exist"
|
||||
|
||||
|
||||
def get_parameters(self):
|
||||
"""Generic function to get all system parameters
|
||||
|
||||
:returns: string -- parameter value
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
return self.data
|
||||
except:
|
||||
return -1
|
||||
|
||||
|
||||
def set_parameter(self, parameter_name, value):
|
||||
"""Generic function to set any system parameter
|
||||
|
||||
:param parameter_name: name of the parameter
|
||||
:param value: value to set
|
||||
:returns: string -- http response of PATCH request
|
||||
|
||||
|
||||
"""
|
||||
# Craft the request
|
||||
action = dict()
|
||||
@ -103,8 +120,8 @@ class Base(object):
|
||||
response = self.api_url.patch(verify=self.connection_parameters.verify_cert,
|
||||
headers={'x-auth-token': self.connection_parameters.auth_token},
|
||||
data=action
|
||||
)
|
||||
return response
|
||||
)
|
||||
return response
|
||||
|
||||
class BaseCollection(Base):
|
||||
"""Abstract class to manage collection (Chassis, Servers etc...)."""
|
||||
@ -112,7 +129,7 @@ class BaseCollection(Base):
|
||||
super(BaseCollection, self).__init__(url, connection_parameters)
|
||||
|
||||
self.links=[]
|
||||
|
||||
|
||||
|
||||
#linksmembers = self.data.Links.Members
|
||||
#linksmembers = self.data.links.Member
|
||||
@ -143,7 +160,7 @@ class Root(Base):
|
||||
version = self.data.RedfishVersion
|
||||
except AttributeError:
|
||||
version = self.data.ServiceVersion
|
||||
|
||||
|
||||
version = version.replace('.', '')
|
||||
version = version[0] + '.' + version[1:]
|
||||
return(version)
|
||||
@ -175,7 +192,7 @@ class Managers(Base):
|
||||
def __init__(self, url, connection_parameters):
|
||||
super(Managers, self).__init__(url, connection_parameters)
|
||||
try:
|
||||
|
||||
|
||||
# self.ethernet_interfaces_collection = EthernetInterfacesCollection(
|
||||
# self.get_link_url("EthernetInterfaces"),
|
||||
# connection_parameters
|
||||
@ -188,12 +205,12 @@ class Managers(Base):
|
||||
)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def get_firmware_version(self):
|
||||
"""Get bios version of the system.
|
||||
|
||||
:returns: string -- bios version
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
# Returned by proliant
|
||||
@ -223,12 +240,12 @@ class Systems(Base):
|
||||
self.bios = Bios(url + "Bios/Settings", connection_parameters)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def reset_system(self):
|
||||
"""Force reset of the system.
|
||||
|
||||
:returns: string -- http response of POST request
|
||||
|
||||
|
||||
"""
|
||||
# Craft the request
|
||||
action = dict()
|
||||
@ -243,12 +260,12 @@ class Systems(Base):
|
||||
)
|
||||
#TODO : treat response.
|
||||
return response
|
||||
|
||||
|
||||
def get_bios_version(self):
|
||||
"""Get bios version of the system.
|
||||
|
||||
:returns: string -- bios version
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
# Returned by proliant
|
||||
@ -262,7 +279,7 @@ class Systems(Base):
|
||||
"""Get serial number of the system.
|
||||
|
||||
:returns: string -- serial number
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
# Returned by proliant
|
||||
@ -271,12 +288,12 @@ class Systems(Base):
|
||||
# Returned by mockup.
|
||||
# Hopefully this kind of discrepencies will be fixed with Redfish 1.0 (August)
|
||||
return ""
|
||||
|
||||
|
||||
def get_power(self):
|
||||
"""Get power status of the system.
|
||||
|
||||
:returns: string -- power status or NULL if there is an issue
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
return self.data.Power
|
||||
@ -288,7 +305,7 @@ class Systems(Base):
|
||||
|
||||
:param value: json structure with value to update
|
||||
:returns: string -- http response of PATCH request
|
||||
|
||||
|
||||
"""
|
||||
# perform the POST action
|
||||
#print self.api_url.url()
|
||||
@ -297,7 +314,7 @@ class Systems(Base):
|
||||
headers={'x-auth-token': self.connection_parameters.auth_token, 'Content-type': 'application/json'},
|
||||
data=value)
|
||||
return response.reason
|
||||
|
||||
|
||||
def set_boot_source_override(self, target, enabled):
|
||||
"""Shotcut function to set boot source
|
||||
|
||||
@ -318,16 +335,16 @@ class Systems(Base):
|
||||
"Once",
|
||||
"Continuous"
|
||||
:returns: string -- http response of PATCH request
|
||||
"""
|
||||
"""
|
||||
return self.set_parameter_json('{"Boot": {"BootSourceOverrideTarget": "'+target+'"},{"BootSourceOverrideEnabled" : "'+enabled+'"}}')
|
||||
|
||||
class SystemsCollection(BaseCollection):
|
||||
"""Class to manage redfish ManagersCollection data."""
|
||||
def __init__(self, url, connection_parameters):
|
||||
super(SystemsCollection, self).__init__(url, connection_parameters)
|
||||
|
||||
|
||||
self.systems_list = []
|
||||
|
||||
|
||||
for link in self.links:
|
||||
self.systems_list.append(Systems(link, connection_parameters))
|
||||
|
||||
@ -341,14 +358,14 @@ class Boot(Base):
|
||||
"""Class to manage redfish Boot data."""
|
||||
def __init__(self, url, connection_parameters):
|
||||
super(Boot, self).__init__(url, connection_parameters)
|
||||
|
||||
|
||||
class EthernetInterfacesCollection(BaseCollection):
|
||||
"""Class to manage redfish EthernetInterfacesColkection data."""
|
||||
def __init__(self, url, connection_parameters):
|
||||
super(EthernetInterfacesCollection, self).__init__(url, connection_parameters)
|
||||
|
||||
|
||||
self.ethernet_interfaces_list = []
|
||||
|
||||
|
||||
# Url returned by the mock up is wrong /redfish/v1/Managers/EthernetInterfaces/1 returns a 404.
|
||||
# The correct one should be /redfish/v1/Managers/1/EthernetInterfaces/1
|
||||
# Check more than 1 hour for this bug.... grrr....
|
||||
@ -357,4 +374,4 @@ class EthernetInterfacesCollection(BaseCollection):
|
||||
|
||||
class EthernetInterfaces(Base):
|
||||
"""Class to manage redfish EthernetInterfaces data."""
|
||||
pass
|
||||
pass
|
||||
|
Loading…
Reference in New Issue
Block a user