From 9a97e84250c4d7f61ef34eceb08f8a086c3f6e0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Ribaud?= Date: Sun, 5 Jul 2015 14:09:14 +0200 Subject: [PATCH 01/18] Update README.rst --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index 266abc2..7855714 100644 --- a/README.rst +++ b/README.rst @@ -47,6 +47,12 @@ Developer setup To initialize a local development environment (eg, so you can run unit tests) you should run the following commands:: +Contacts +-------- + +Distribution list : python-redfish@mondorescue.org + + Further References ------------------ From cdc91e29c36b820dc5f4078833babe866eeaf15e Mon Sep 17 00:00:00 2001 From: Bruno Cornec Date: Fri, 11 Sep 2015 00:50:35 +0200 Subject: [PATCH 02/18] Suppress a useless Dockerfile not used yet which contains private info --- examples/docker/Dockerfile | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 examples/docker/Dockerfile diff --git a/examples/docker/Dockerfile b/examples/docker/Dockerfile deleted file mode 100644 index 041ad08..0000000 --- a/examples/docker/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM ubuntu:15.04 -MAINTAINER bruno.cornec@hp.com -ENV DEBIAN_FRONTEND noninterative -ENV http_proxy http://web-proxy.fra.hp.com:8080 -ENV https_proxy http://web-proxy.fra.hp.com:8080 -# Install deps for Redfish mockup -RUN apt-get update -RUN apt-get -y install python-mock python-pip git openssh-client libpython2.7-dev python-oslotest -RUN apt-get -y ansible -RUN useradd -m bruno -RUN chown -R bruno /usr/local -RUN su - bruno -c "git clone https://github.com/bcornec/python-redfish.git ; pip install -r python-redfish/requirements.txt ; cd python-redfish ; python setup.py install -O1" -CMD /bin/bash From 19b64e8734402f3950813c18ac59a9ae7f4f147e Mon Sep 17 00:00:00 2001 From: Bruno Cornec Date: Tue, 24 Nov 2015 14:34:51 +0100 Subject: [PATCH 03/18] Ignore vim files in git --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 1d9192b..57a8ee7 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,7 @@ target/ .pydevproject .settings/ .metadata + +# Vim +*~ +*.sw? From 6b2728e5d86eea07e128b01a0e12972f0c88aaf9 Mon Sep 17 00:00:00 2001 From: Bruno Cornec Date: Wed, 26 Aug 2015 17:05:54 +0200 Subject: [PATCH 04/18] Review spec file for local usage --- python-redfish.spec | 1 - 1 file changed, 1 deletion(-) diff --git a/python-redfish.spec b/python-redfish.spec index d6c3171..e60f04d 100644 --- a/python-redfish.spec +++ b/python-redfish.spec @@ -28,7 +28,6 @@ system such as defined by http://www.redfishcertification.org %install %{__python} setup.py install -O1 --skip-build --root %{buildroot} -# TODO: Add examples %files %doc README.rst examples/*.py %dir %{python_sitelib}/redfish From a3755a10712b30a2dca8a0d230de109d840f2e6e Mon Sep 17 00:00:00 2001 From: Uggla Date: Wed, 23 Sep 2015 23:52:11 +0200 Subject: [PATCH 05/18] Fix to handle Redfish v1.0 session login. --- redfish/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/redfish/main.py b/redfish/main.py index 5736732..4f93db6 100644 --- a/redfish/main.py +++ b/redfish/main.py @@ -11,6 +11,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +from redfish import mapping """ STARTING ASSUMPTIONS @@ -285,6 +286,11 @@ class RedfishConnection(object): url = self.Root.get_link_url( mapping.redfish_mapper.map_sessionservice() ) + + # 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} From d3506f6a2ed4505bed92b646bc8c964608529f2e Mon Sep 17 00:00:00 2001 From: Uggla Date: Wed, 23 Sep 2015 23:52:58 +0200 Subject: [PATCH 06/18] Handle login exception. This is a first implementation that needs more elaborate error msg. --- redfish/exception.py | 15 ++++++++++----- redfish/main.py | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/redfish/exception.py b/redfish/exception.py index f6f759a..6038480 100644 --- a/redfish/exception.py +++ b/redfish/exception.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- - +import sys +import config class RedfishException(Exception): """Base class for redfish exceptions""" @@ -8,9 +9,13 @@ class RedfishException(Exception): self.message = message - class AuthenticationFailureException(RedfishException): - pass - + 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) + class LogoutFailureException(RedfishException): - pass \ No newline at end of file + pass diff --git a/redfish/main.py b/redfish/main.py index 4f93db6..f7e6e5e 100644 --- a/redfish/main.py +++ b/redfish/main.py @@ -313,7 +313,7 @@ class RedfishConnection(object): # TODO : Manage exception with a class. # ======================================================================= if auth.status_code != 201: - pass + raise exception.AuthenticationFailureException("Login request return an invalid status code") #sysraise "Error getting token", auth.status_code self.connection_parameters.auth_token = auth.headers.get("x-auth-token") From c48f23979261fd5a7291249c591a84c8cb6999ab Mon Sep 17 00:00:00 2001 From: vmisson Date: Thu, 24 Sep 2015 16:00:50 +0200 Subject: [PATCH 07/18] Create new function: get_serialnumber. Tested with Simulator (v1) + Proliant v0.9.5 + Proliant v1.0 --- examples/simple-proliant.py | 2 ++ redfish/types.py | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/examples/simple-proliant.py b/examples/simple-proliant.py index 9645b34..ca481c3 100644 --- a/examples/simple-proliant.py +++ b/examples/simple-proliant.py @@ -42,4 +42,6 @@ print ("Redfish API version : %s \n" % remote_mgmt.get_api_version()) print("Bios version : {}\n".format(remote_mgmt.Systems.systems_list[0].get_bios_version())) +print("Serial Number : {}\n".format(remote_mgmt.Systems.systems_list[0].get_serialnumber())) + remote_mgmt.logout() diff --git a/redfish/types.py b/redfish/types.py index d462731..80185dd 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -186,6 +186,15 @@ class Systems(Base): # Hopefully this kind of discrepencies will be fixed with Redfish 1.0 (August) return self.data.BiosVersion + def get_serialnumber(self): + try: + # Returned by proliant + return self.data.SerialNumber + except: + # Returned by mockup. + # Hopefully this kind of discrepencies will be fixed with Redfish 1.0 (August) + return "" + class SystemsCollection(BaseCollection): """Class to manage redfish ManagersCollection data.""" From 4cae02c3c959cab5c0a0bb47a2dd037cc01c225e Mon Sep 17 00:00:00 2001 From: vmisson Date: Fri, 25 Sep 2015 00:47:03 +0200 Subject: [PATCH 08/18] New function get_power() and generic function get_parameter(parameter_name) for class Systems get_power(): return power state of the system get_parameter(parameter_name): return parameter value based on the parameter_name --- examples/simple-proliant.py | 4 +++- redfish/types.py | 14 +++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/simple-proliant.py b/examples/simple-proliant.py index ca481c3..d0c7e61 100644 --- a/examples/simple-proliant.py +++ b/examples/simple-proliant.py @@ -41,7 +41,9 @@ print ("Redfish API version : %s \n" % remote_mgmt.get_api_version()) # remote_mgmt.Systems.systems_list[0] = remote_mgmt.Systems.managed_system print("Bios version : {}\n".format(remote_mgmt.Systems.systems_list[0].get_bios_version())) +print("Serial Number : {}\n".format(remote_mgmt.Systems.systems_list[0].get_serial_number())) +print("Power State : {}\n".format(remote_mgmt.Systems.systems_list[0].get_power())) +print("Parameter 'Model' : {}\n".format(remote_mgmt.Systems.systems_list[0].get_parameter("Model"))) -print("Serial Number : {}\n".format(remote_mgmt.Systems.systems_list[0].get_serialnumber())) remote_mgmt.logout() diff --git a/redfish/types.py b/redfish/types.py index 80185dd..ef13b7d 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -186,7 +186,7 @@ class Systems(Base): # Hopefully this kind of discrepencies will be fixed with Redfish 1.0 (August) return self.data.BiosVersion - def get_serialnumber(self): + def get_serial_number(self): try: # Returned by proliant return self.data.SerialNumber @@ -194,6 +194,18 @@ class Systems(Base): # Returned by mockup. # Hopefully this kind of discrepencies will be fixed with Redfish 1.0 (August) return "" + + def get_power(self): + try: + return self.data.Power + except: + return "" + + def get_parameter(self, parameter_name): + try: + return self.data[parameter_name] + except: + return "Parameter does not exist" class SystemsCollection(BaseCollection): From 50abb5cda25b91328dfa32748114e7ed8de00330 Mon Sep 17 00:00:00 2001 From: Uggla Date: Fri, 25 Sep 2015 23:46:34 +0200 Subject: [PATCH 09/18] Remove import mistake coming from pydev automatic import. --- redfish/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redfish/main.py b/redfish/main.py index f7e6e5e..fbe9f21 100644 --- a/redfish/main.py +++ b/redfish/main.py @@ -11,7 +11,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from redfish import mapping + """ STARTING ASSUMPTIONS From 394aee2f2bcebad937ea5c9219a21f0c93d4b17c Mon Sep 17 00:00:00 2001 From: Uggla Date: Fri, 25 Sep 2015 23:47:50 +0200 Subject: [PATCH 10/18] Initial commit of redfish-client. --- redfish-client/redfish-client.py | 73 ++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 redfish-client/redfish-client.py diff --git a/redfish-client/redfish-client.py b/redfish-client/redfish-client.py new file mode 100644 index 0000000..a6f9398 --- /dev/null +++ b/redfish-client/redfish-client.py @@ -0,0 +1,73 @@ +# coding=utf-8 + +""" +redfish-client + +Usage: + redfish-client.py [options] config add [] [] + redfish-client.py [options] config del + redfish-client.py [options] config modify (url | login | password) + redfish-client.py [options] config show + redfish-client.py [options] config showall + redfish-client.py (-h | --help) + redfish-client.py --version + + +Options: + -h --help Show this screen. + --version Show version. + --conf_file FILE Configuration file [default: ~/.redfish.conf]. + + +config commands manage the configuration file. + +""" + +import os +import sys +import json +import pprint +import docopt + + +class ConfigFile(object): + def __init__(self, config_file): + # read json file + try: + with open(config_file) as json_data: + self.data = json.load(json_data) + json_data.close() + except IOError as e: + self.data = {"Nodes":{}} + + + +if __name__ == '__main__': + # Get $HOME environment. + HOME = os.getenv('HOME') + + if HOME == '': + print("$HOME environment variable not set, please check your system") + sys.exit(1) + + arguments = docopt.docopt(__doc__, version='redfish-client 0.1') + print(arguments) + + arguments['--conf_file'] = arguments['--conf_file'].replace('~', HOME) + + conf_file = ConfigFile(arguments['--conf_file']) + + + if arguments['config'] == True: + if arguments['showall'] == True: + pprint.pprint(conf_file.data) + elif arguments['add'] == True: + conf_file.data['Nodes'][arguments['']] = {} + conf_file.data['Nodes'][arguments['']]['url'] = arguments[''] + if arguments[''] != None: + conf_file.data['Nodes'][arguments['']]['login'] = arguments[''] + if arguments[''] != None: + conf_file.data['Nodes'][arguments['']]['password'] = arguments[''] + pprint.pprint(conf_file.data) + + \ No newline at end of file From 69122fa26dcb2adff93fae697b6e46001cabb58d Mon Sep 17 00:00:00 2001 From: vmisson Date: Mon, 28 Sep 2015 00:20:41 +0200 Subject: [PATCH 11/18] New classes in types.py: Bios & Boot Creating Bios class to manage System/Bios: - get_parameters() - get_parameter(parameter_name) - set_parameter(parameter_name, value) Creating Boot class to manage System/Bios/Boot: - get_parameters() - get_parameter(parameter_name) - set_parameter(parameter_name, value) New tests in simple-porliant.py file --- examples/simple-proliant.py | 21 +++++++++++- redfish/types.py | 66 +++++++++++++++++++++++++++++++++---- 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/examples/simple-proliant.py b/examples/simple-proliant.py index d0c7e61..e8e863c 100644 --- a/examples/simple-proliant.py +++ b/examples/simple-proliant.py @@ -6,6 +6,7 @@ import os import sys import json import redfish +from time import sleep # Get $HOME environment. HOME = os.getenv('HOME') @@ -43,7 +44,25 @@ print ("Redfish API version : %s \n" % remote_mgmt.get_api_version()) print("Bios version : {}\n".format(remote_mgmt.Systems.systems_list[0].get_bios_version())) print("Serial Number : {}\n".format(remote_mgmt.Systems.systems_list[0].get_serial_number())) print("Power State : {}\n".format(remote_mgmt.Systems.systems_list[0].get_power())) -print("Parameter 'Model' : {}\n".format(remote_mgmt.Systems.systems_list[0].get_parameter("Model"))) +print("Parameter 'SystemType' : {}\n".format(remote_mgmt.Systems.systems_list[0].get_parameter("SystemType"))) +print("Get bios parameters : {}\n".format(remote_mgmt.Systems.systems_list[0].bios.get_parameters())) +print("Get boot parameters : {}\n".format(remote_mgmt.Systems.systems_list[0].bios.boot.get_parameters())) + + +print("Get bios parameter 'AdminPhone' : {}\n".format(remote_mgmt.Systems.systems_list[0].bios.get_parameter("AdminPhone"))) +print("Set bios parameter 'AdminPhone' to '' : {}\n".format(remote_mgmt.Systems.systems_list[0].bios.set_parameter("AdminPhone",""))) + + +#Boot server with script +#remote_mgmt.Systems.systems_list[0].bios.set_parameter("PreBootNetwork","Auto") +#remote_mgmt.Systems.systems_list[0].bios.set_parameter("Dhcpv4","Enabled") + +#remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartup","Enabled") +#remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupLocation","NetworkLocation") +#remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupUrl","http://192.168.1.1/deploy/startup.nsh") + + +#remote_mgmt.Systems.systems_list[0].reset_system() remote_mgmt.logout() diff --git a/redfish/types.py b/redfish/types.py index ef13b7d..01a1d91 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -6,6 +6,7 @@ import requests import tortilla import config import mapping +import re # Global variable @@ -162,6 +163,7 @@ class Systems(Base): # Also to check with the ironic driver requirement. def __init__(self, url, connection_parameters): super(Systems, self).__init__(url, connection_parameters) + self.bios = Bios(url+"Bios/Settings", connection_parameters) def reset_system(self): # Craft the request @@ -174,7 +176,7 @@ class Systems(Base): response = self.api_url.post(verify=self.connection_parameters.verify_cert, headers={'x-auth-token': self.connection_parameters.auth_token}, data=action - ) + ) #TODO : treat response. def get_bios_version(self): @@ -206,8 +208,7 @@ class Systems(Base): return self.data[parameter_name] except: return "Parameter does not exist" - - + class SystemsCollection(BaseCollection): """Class to manage redfish ManagersCollection data.""" def __init__(self, url, connection_parameters): @@ -217,8 +218,62 @@ class SystemsCollection(BaseCollection): for link in self.links: self.systems_list.append(Systems(link, connection_parameters)) - - + +class Bios(Base): + def __init__(self, url, connection_parameters): + super(Bios, self).__init__(url, connection_parameters) + self.boot = Boot(re.findall(".+/Bios",url)[0]+"/Boot/Settings", connection_parameters) + + def get_parameters(self): + try: + return self.data + except: + return -1 + + def get_parameter(self, parameter_name): + try: + return self.data[parameter_name] + except: + return "Parameter does not exist" + + def set_parameter(self, parameter_name, value): + # Craft the request + action = dict() + action[parameter_name] = value + + # perform the POST action + print self.api_url + response = self.api_url.patch(verify=self.connection_parameters.verify_cert, + headers={'x-auth-token': self.connection_parameters.auth_token}, + data=action + ) +class Boot(Base): + def __init__(self, url, connection_parameters): + super(Boot, self).__init__(url, connection_parameters) + + def get_parameters(self): + try: + return self.data + except: + return -1 + + def get_parameter(self, parameter_name): + try: + return self.data[parameter_name] + except: + return "Parameter does not exist" + + def set_parameter(self, parameter_name, value): + # Craft the request + action = dict() + action[parameter_name] = value + + # perform the POST action + response = self.api_url.patch(verify=self.connection_parameters.verify_cert, + headers={'x-auth-token': self.connection_parameters.auth_token}, + data=action + ) + class EthernetInterfacesCollection(BaseCollection): def __init__(self, url, connection_parameters): super(EthernetInterfacesCollection, self).__init__(url, connection_parameters) @@ -231,6 +286,5 @@ class EthernetInterfacesCollection(BaseCollection): for link in self.links: self.ethernet_interfaces_list.append(EthernetInterfaces(link, connection_parameters)) - class EthernetInterfaces(Base): pass \ No newline at end of file From 6bc483a465e3b6c463cfe247624d531bb4737f9b Mon Sep 17 00:00:00 2001 From: vmisson Date: Mon, 5 Oct 2015 11:15:29 +0200 Subject: [PATCH 12/18] New function set_parameter and set_parameter_json on Systems Class - set_parameter(parameter_name, value): change parameter_name to value. Restart is required to apply the changes. - set_parameter_json(json): update System with the json. Restart is required to apply the changes. New tests were added to simple-proliat file to update boot value. --- examples/simple-proliant.py | 16 ++++++++++------ redfish/types.py | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/examples/simple-proliant.py b/examples/simple-proliant.py index e8e863c..a129cba 100644 --- a/examples/simple-proliant.py +++ b/examples/simple-proliant.py @@ -50,19 +50,23 @@ print("Get bios parameters : {}\n".format(remote_mgmt.Systems.systems_list[0].bi print("Get boot parameters : {}\n".format(remote_mgmt.Systems.systems_list[0].bios.boot.get_parameters())) -print("Get bios parameter 'AdminPhone' : {}\n".format(remote_mgmt.Systems.systems_list[0].bios.get_parameter("AdminPhone"))) -print("Set bios parameter 'AdminPhone' to '' : {}\n".format(remote_mgmt.Systems.systems_list[0].bios.set_parameter("AdminPhone",""))) +#print("Get bios parameter 'AdminPhone' : {}\n".format(remote_mgmt.Systems.systems_list[0].bios.get_parameter("AdminPhone"))) +#print("Set bios parameter 'AdminPhone' to '' : {}\n".format(remote_mgmt.Systems.systems_list[0].bios.set_parameter("AdminPhone",""))) #Boot server with script -#remote_mgmt.Systems.systems_list[0].bios.set_parameter("PreBootNetwork","Auto") #remote_mgmt.Systems.systems_list[0].bios.set_parameter("Dhcpv4","Enabled") -#remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartup","Enabled") -#remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupLocation","NetworkLocation") -#remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupUrl","http://192.168.1.1/deploy/startup.nsh") +remote_mgmt.Systems.systems_list[0].bios.set_parameter("PreBootNetwork", "Auto") +remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartup", "Enabled") +remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupLocation", "NetworkLocation") +remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupUrl", "http://10.3.222.88/deploy/startup.nsh") +remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideTarget": "UefiShell"}}') +remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Once"}}') + +#Reset of the system is required to apply the changes #remote_mgmt.Systems.systems_list[0].reset_system() remote_mgmt.logout() diff --git a/redfish/types.py b/redfish/types.py index 01a1d91..9ef5427 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -209,6 +209,29 @@ class Systems(Base): except: return "Parameter does not exist" + def set_parameter(self, parameter_name, value): + # Craft the request + action = dict() + action[parameter_name] = value + print(action) + + # perform the POST action + print self.api_url + response = self.api_url.patch(verify=self.connection_parameters.verify_cert, + headers={'x-auth-token': self.connection_parameters.auth_token}, + data=action + ) + + def set_parameter_json(self, value): + # perform the POST action + print self.api_url.url() + + response = requests.patch(self.api_url.url(), + verify=self.connection_parameters.verify_cert, + headers={'x-auth-token': self.connection_parameters.auth_token, 'Content-type': 'application/json'}, + data=value) + print(response.reason) + class SystemsCollection(BaseCollection): """Class to manage redfish ManagersCollection data.""" def __init__(self, url, connection_parameters): From 6b296a79a385033d67d718496dfe9cf2dad15b84 Mon Sep 17 00:00:00 2001 From: vmisson Date: Wed, 14 Oct 2015 18:39:06 +0200 Subject: [PATCH 13/18] Add comment --- examples/simple-proliant.py | 3 +- redfish/types.py | 109 ++++++++++++++++++++++++++++++++---- 2 files changed, 100 insertions(+), 12 deletions(-) diff --git a/examples/simple-proliant.py b/examples/simple-proliant.py index a129cba..ef0b3fe 100644 --- a/examples/simple-proliant.py +++ b/examples/simple-proliant.py @@ -62,11 +62,10 @@ remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartup", "Enab remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupLocation", "NetworkLocation") remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupUrl", "http://10.3.222.88/deploy/startup.nsh") - remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideTarget": "UefiShell"}}') remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Once"}}') #Reset of the system is required to apply the changes -#remote_mgmt.Systems.systems_list[0].reset_system() +remote_mgmt.Systems.systems_list[0].reset_system() remote_mgmt.logout() diff --git a/redfish/types.py b/redfish/types.py index 9ef5427..4d9ec3c 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -15,6 +15,7 @@ class Base(object): """Abstract class to manage types (Chassis, Servers etc...).""" def __init__(self, url, connection_parameters): + """Class constructor""" global TORTILLADEBUG self.connection_parameters = connection_parameters # Uggly hack to check self.url = url @@ -91,7 +92,6 @@ class BaseCollection(Base): class Root(Base): """Class to manage redfish Root data.""" - def get_api_version(self): """Return api version. @@ -109,6 +109,11 @@ class Root(Base): return(version) def get_api_UUID(self): + """Return UUID version. + + :returns: string -- UUID + + """ return self.data.UUID @@ -127,6 +132,7 @@ class SessionService(Base): class Managers(Base): + """Class to manage redfish Managers.""" def __init__(self, url, connection_parameters): super(Managers, self).__init__(url, connection_parameters) @@ -149,37 +155,47 @@ class Managers(Base): class ManagersCollection(BaseCollection): """Class to manage redfish ManagersCollection data.""" def __init__(self, url, connection_parameters): + """Class constructor""" super(ManagersCollection, self).__init__(url, connection_parameters) - self.managers_list = [] - for link in self.links: self.managers_list.append(Managers(link, connection_parameters)) - - class Systems(Base): + """Class to manage redfish Systems data.""" # TODO : Need to discuss with Bruno the required method. # Also to check with the ironic driver requirement. def __init__(self, url, connection_parameters): + """Class constructor""" super(Systems, self).__init__(url, connection_parameters) self.bios = Bios(url+"Bios/Settings", connection_parameters) def reset_system(self): + """Force reset of the system. + + :returns: string -- http response of POST request + + """ # Craft the request action = dict() action['Action'] = 'Reset' action['ResetType'] = 'ForceRestart' - # perform the POST action - print self.api_url + #Debug the url and perform the POST action + #print self.api_url response = self.api_url.post(verify=self.connection_parameters.verify_cert, headers={'x-auth-token': self.connection_parameters.auth_token}, data=action ) #TODO : treat response. - + return response + def get_bios_version(self): + """Get bios version of the system. + + :returns: string -- bios version + + """ try: # Returned by proliant return self.data.Bios.Current.VersionString @@ -189,6 +205,11 @@ class Systems(Base): return self.data.BiosVersion def get_serial_number(self): + """Get serial number of the system. + + :returns: string -- serial number + + """ try: # Returned by proliant return self.data.SerialNumber @@ -198,31 +219,56 @@ class Systems(Base): 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 except: return "" 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 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() action[parameter_name] = value print(action) - # perform the POST action + # Perform the POST action print self.api_url response = self.api_url.patch(verify=self.connection_parameters.verify_cert, headers={'x-auth-token': self.connection_parameters.auth_token}, data=action - ) + ) + return response def set_parameter_json(self, value): + """Generic function to set any system parameter using json structure + + :param value: json structure with value to update + :returns: string -- http response of PATCH request + + """ # perform the POST action print self.api_url.url() @@ -243,23 +289,42 @@ class SystemsCollection(BaseCollection): self.systems_list.append(Systems(link, connection_parameters)) class Bios(Base): + """Class to manage redfish Bios data.""" def __init__(self, url, connection_parameters): super(Bios, self).__init__(url, connection_parameters) self.boot = Boot(re.findall(".+/Bios",url)[0]+"/Boot/Settings", connection_parameters) def get_parameters(self): + """Generic function to get all system parameters + + :returns: string -- parameter value + + """ try: return self.data except: return -1 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 set_parameter(self, parameter_name, value): + """Generic function to set any bios 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() action[parameter_name] = value @@ -270,23 +335,44 @@ class Bios(Base): headers={'x-auth-token': self.connection_parameters.auth_token}, data=action ) + return response + class Boot(Base): + """Class to manage redfish Boot data.""" def __init__(self, url, connection_parameters): super(Boot, self).__init__(url, connection_parameters) def get_parameters(self): + """Generic function to get all system parameters + + :returns: string -- parameter value + + """ try: return self.data except: return -1 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 set_parameter(self, parameter_name, value): + """Generic function to set any bios 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() action[parameter_name] = value @@ -296,8 +382,10 @@ class Boot(Base): headers={'x-auth-token': self.connection_parameters.auth_token}, data=action ) + return response class EthernetInterfacesCollection(BaseCollection): + """Class to manage redfish EthernetInterfacesColkection data.""" def __init__(self, url, connection_parameters): super(EthernetInterfacesCollection, self).__init__(url, connection_parameters) @@ -310,4 +398,5 @@ class EthernetInterfacesCollection(BaseCollection): self.ethernet_interfaces_list.append(EthernetInterfaces(link, connection_parameters)) class EthernetInterfaces(Base): + """Class to manage redfish EthernetInterfaces data.""" pass \ No newline at end of file From dd6a1eafc05e6383e563f8d402902cbb1bf1f07d Mon Sep 17 00:00:00 2001 From: Uggla Date: Thu, 15 Oct 2015 13:54:17 +0200 Subject: [PATCH 14/18] Add UEFI mode. --- examples/simple-proliant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple-proliant.py b/examples/simple-proliant.py index ef0b3fe..ce33940 100644 --- a/examples/simple-proliant.py +++ b/examples/simple-proliant.py @@ -63,7 +63,7 @@ remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupLocation remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupUrl", "http://10.3.222.88/deploy/startup.nsh") remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideTarget": "UefiShell"}}') -remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Once"}}') +remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Continuous"}}') #Reset of the system is required to apply the changes remote_mgmt.Systems.systems_list[0].reset_system() From 2a8d261b41fd95fedf5539aa794aa89ca75831ea Mon Sep 17 00:00:00 2001 From: Uggla Date: Thu, 15 Oct 2015 13:55:28 +0200 Subject: [PATCH 15/18] Add both UEFI parameters "Continuous" and "Once" as an example. --- examples/simple-proliant.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/simple-proliant.py b/examples/simple-proliant.py index ce33940..22cf8dd 100644 --- a/examples/simple-proliant.py +++ b/examples/simple-proliant.py @@ -33,7 +33,7 @@ remote_mgmt = redfish.connect(URL, USER_NAME, PASSWORD, verify_cert=False) print ("Redfish API version : %s \n" % remote_mgmt.get_api_version()) -# Uncomment following line to reset the blade !!! +# Uncomment following line to reset the blade !!! #remote_mgmt.Systems.systems_list[0].reset_system() # TODO : create an attribute to link the managed system directly @@ -63,7 +63,8 @@ remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupLocation remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupUrl", "http://10.3.222.88/deploy/startup.nsh") remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideTarget": "UefiShell"}}') -remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Continuous"}}') +# remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Continuous"}}') +remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Once"}}') #Reset of the system is required to apply the changes remote_mgmt.Systems.systems_list[0].reset_system() From 6eaccc3ad262718f225795ded33ca23e080fac0f Mon Sep 17 00:00:00 2001 From: Uggla Date: Thu, 15 Oct 2015 14:01:45 +0200 Subject: [PATCH 16/18] Add LICENCE file. Apache 2 Licence. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index e06d208..8f71f43 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ From 065a61442ab24ee0dbde1bbf1700eec676347a0a Mon Sep 17 00:00:00 2001 From: Uggla Date: Fri, 16 Oct 2015 00:40:36 +0200 Subject: [PATCH 17/18] Update redfish-client.py --- redfish-client/redfish-client.py | 93 +++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 20 deletions(-) mode change 100644 => 100755 redfish-client/redfish-client.py diff --git a/redfish-client/redfish-client.py b/redfish-client/redfish-client.py old mode 100644 new mode 100755 index a6f9398..c1be6b6 --- a/redfish-client/redfish-client.py +++ b/redfish-client/redfish-client.py @@ -1,3 +1,5 @@ +#!/usr/bin/python + # coding=utf-8 """ @@ -19,7 +21,7 @@ Options: --conf_file FILE Configuration file [default: ~/.redfish.conf]. -config commands manage the configuration file. +config commands manage the configuration file. """ @@ -32,17 +34,65 @@ import docopt class ConfigFile(object): def __init__(self, config_file): + self._config_file = config_file # read json file - try: - with open(config_file) as json_data: + try: + with open(self._config_file) as json_data: self.data = json.load(json_data) json_data.close() - except IOError as e: - self.data = {"Nodes":{}} + except (ValueError, IOError): + self.data = {"Managers":{}} + + def save(self): + try: + with open(self._config_file , 'w') as json_data: + json.dump(self.data, json_data) + json_data.close() + except IOError as e: + print(e.msg) + sys.exit(1) + + def add_manager(self, manager_name, url, login, password): + self.data['Managers'][manager_name] = {} + self.data['Managers'][manager_name]['url'] = url + if login != None: + self.data['Managers'][manager_name]['login'] = login + if password != None: + self.data['Managers'][manager_name]['password'] = password + + def get_managers(self): + managers = [] + for manager in self.data['Managers']: + managers += [manager] + return(managers) + + def get_manager_info(self, manager): + info = {} + url=self.data['Managers'][manager]['url'] + login=self.data['Managers'][manager]['login'] + password=self.data['Managers'][manager]['password'] + info={'url':url, 'login':login, 'password':password} + return(info) + +class RedfishClientException(Exception): + """Base class for redfish client exceptions""" + def __init__(self, message=None, **kwargs): + self.kwargs = kwargs + self.message = message - if __name__ == '__main__': + # Functions + def show_manager(all=False): + print("Managers configured :") + for manager in conf_file.get_managers(): + print(manager) + if all == True: + info = conf_file.get_manager_info(manager) + print("\tUrl : {}".format(info['url'])) + print("\tLogin : {}".format(info['login'])) + print("\tPassword : {}".format(info['password'])) + # Get $HOME environment. HOME = os.getenv('HOME') @@ -52,22 +102,25 @@ if __name__ == '__main__': arguments = docopt.docopt(__doc__, version='redfish-client 0.1') print(arguments) - + arguments['--conf_file'] = arguments['--conf_file'].replace('~', HOME) - + conf_file = ConfigFile(arguments['--conf_file']) - - + + if arguments['config'] == True: - if arguments['showall'] == True: - pprint.pprint(conf_file.data) + if arguments['show'] == True: + show_manager() + elif arguments['showall'] == True: + show_manager(True) elif arguments['add'] == True: - conf_file.data['Nodes'][arguments['']] = {} - conf_file.data['Nodes'][arguments['']]['url'] = arguments[''] - if arguments[''] != None: - conf_file.data['Nodes'][arguments['']]['login'] = arguments[''] - if arguments[''] != None: - conf_file.data['Nodes'][arguments['']]['password'] = arguments[''] + conf_file.add_manager(arguments[''], + arguments[''], + arguments[''], + arguments['password']) pprint.pprint(conf_file.data) - - \ No newline at end of file + + conf_file.save() + + + sys.exit(0) From e92169956ffd267e69d19839950ca8bfd382caae Mon Sep 17 00:00:00 2001 From: Uggla Date: Thu, 5 Nov 2015 10:18:05 +0100 Subject: [PATCH 18/18] Merge branch 'prototype' of https://github.com/vmisson/python-redfish into prototype. Quick fix to allow library to run on the mockup. Update the simple-simulator example. Update redfish-client. --- examples/simple-proliant.py | 15 ++- examples/simple-simulator.py | 10 +- redfish/types.py | 212 ++++++++++++++--------------------- 3 files changed, 103 insertions(+), 134 deletions(-) diff --git a/examples/simple-proliant.py b/examples/simple-proliant.py index 22cf8dd..a8f5174 100644 --- a/examples/simple-proliant.py +++ b/examples/simple-proliant.py @@ -62,11 +62,20 @@ remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartup", "Enab remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupLocation", "NetworkLocation") remote_mgmt.Systems.systems_list[0].bios.set_parameter("UefiShellStartupUrl", "http://10.3.222.88/deploy/startup.nsh") -remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideTarget": "UefiShell"}}') +#remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideTarget": "UefiShell"}}') # remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Continuous"}}') -remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Once"}}') +#remote_mgmt.Systems.systems_list[0].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Once"}}') + +mySystem = remote_mgmt.Systems.systems_list[0] +mySystem.set_boot_source_override("None","Disabled") +#Uncomment the next line to reset the server +#mySystem.reset_system() + + +print("Get manager firmware version : {}\n".format(remote_mgmt.Managers.managers_list[0].get_firmware_version())) +print("Get system Bios version : {}\n".format(remote_mgmt.Systems.systems_list[0].get_bios_version())) #Reset of the system is required to apply the changes -remote_mgmt.Systems.systems_list[0].reset_system() +#remote_mgmt.Systems.systems_list[0].reset_system() remote_mgmt.logout() diff --git a/examples/simple-simulator.py b/examples/simple-simulator.py index c31a0f7..52d9f18 100644 --- a/examples/simple-simulator.py +++ b/examples/simple-simulator.py @@ -31,8 +31,10 @@ PASSWORD = config["Nodes"]["default"]["password"] remote_mgmt = redfish.connect(URL, USER_NAME, PASSWORD, simulator=True, enforceSSL=False) -print ("Redfish API version : {} \n".format(remote_mgmt.get_api_version())) -print ("UUID : {} \n".format(remote_mgmt.Root.get_api_UUID())) -print ("Bios version : {}\n".format(remote_mgmt.Systems.systems_list[0].get_bios_version())) - +print("Redfish API version : {} \n".format(remote_mgmt.get_api_version())) +print("UUID : {} \n".format(remote_mgmt.Root.get_api_UUID())) +print("System 1 :\n") +print("Bios version : {}\n".format(remote_mgmt.Systems.systems_list[0].get_bios_version())) +print("System 2 :\n") +print("Bios version : {}\n".format(remote_mgmt.Systems.systems_list[1].get_parameter("SerialNumber"))) #print remoteMgmt.get_api_link_to_server() diff --git a/redfish/types.py b/redfish/types.py index 4d9ec3c..d6b3717 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -13,7 +13,6 @@ import re class Base(object): """Abstract class to manage types (Chassis, Servers etc...).""" - def __init__(self, url, connection_parameters): """Class constructor""" global TORTILLADEBUG @@ -62,11 +61,53 @@ 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() + action[parameter_name] = value + print(action) + + # Perform the POST action + print self.api_url + response = self.api_url.patch(verify=self.connection_parameters.verify_cert, + headers={'x-auth-token': self.connection_parameters.auth_token}, + data=action + ) + return response class BaseCollection(Base): """Abstract class to manage collection (Chassis, Servers etc...).""" - def __init__(self, url, connection_parameters): super(BaseCollection, self).__init__(url, connection_parameters) @@ -91,7 +132,6 @@ class BaseCollection(Base): class Root(Base): """Class to manage redfish Root data.""" - def get_api_version(self): """Return api version. @@ -116,7 +156,6 @@ class Root(Base): """ return self.data.UUID - def get_api_link_to_server(self): """Return api link to server. @@ -135,7 +174,6 @@ class Managers(Base): """Class to manage redfish Managers.""" def __init__(self, url, connection_parameters): super(Managers, self).__init__(url, connection_parameters) - try: # self.ethernet_interfaces_collection = EthernetInterfacesCollection( @@ -150,7 +188,20 @@ class Managers(Base): ) except: pass + + def get_firmware_version(self): + """Get bios version of the system. + :returns: string -- bios version + + """ + try: + # Returned by proliant + return self.data.FirmwareVersion + except: + # Returned by mockup. + # Hopefully this kind of discrepencies will be fixed with Redfish 1.0 (August) + return self.data.FirmwareVersion class ManagersCollection(BaseCollection): """Class to manage redfish ManagersCollection data.""" @@ -168,7 +219,10 @@ class Systems(Base): def __init__(self, url, connection_parameters): """Class constructor""" super(Systems, self).__init__(url, connection_parameters) - self.bios = Bios(url+"Bios/Settings", connection_parameters) + try: + self.bios = Bios(url + "Bios/Settings", connection_parameters) + except: + pass def reset_system(self): """Force reset of the system. @@ -228,40 +282,7 @@ class Systems(Base): return self.data.Power except: return "" - - 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 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() - action[parameter_name] = value - print(action) - - # Perform the POST action - print self.api_url - response = self.api_url.patch(verify=self.connection_parameters.verify_cert, - headers={'x-auth-token': self.connection_parameters.auth_token}, - data=action - ) - return response - def set_parameter_json(self, value): """Generic function to set any system parameter using json structure @@ -270,13 +291,35 @@ class Systems(Base): """ # perform the POST action - print self.api_url.url() - + #print self.api_url.url() response = requests.patch(self.api_url.url(), verify=self.connection_parameters.verify_cert, headers={'x-auth-token': self.connection_parameters.auth_token, 'Content-type': 'application/json'}, data=value) - print(response.reason) + return response.reason + + def set_boot_source_override(self, target, enabled): + """Shotcut function to set boot source + + :param target: new boot source. Supported values: + "None", + "Pxe", + "Floppy", + "Cd", + "Usb", + "Hdd", + "BiosSetup", + "Utilities", + "Diags", + "UefiShell", + "UefiTarget" + :param enabled: Supported values: + "Disabled", + "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.""" @@ -293,96 +336,11 @@ class Bios(Base): def __init__(self, url, connection_parameters): super(Bios, self).__init__(url, connection_parameters) self.boot = Boot(re.findall(".+/Bios",url)[0]+"/Boot/Settings", connection_parameters) - - def get_parameters(self): - """Generic function to get all system parameters - :returns: string -- parameter value - - """ - try: - return self.data - except: - return -1 - - 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 set_parameter(self, parameter_name, value): - """Generic function to set any bios 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() - action[parameter_name] = value - - # perform the POST action - print self.api_url - response = self.api_url.patch(verify=self.connection_parameters.verify_cert, - headers={'x-auth-token': self.connection_parameters.auth_token}, - data=action - ) - return response - class Boot(Base): """Class to manage redfish Boot data.""" def __init__(self, url, connection_parameters): super(Boot, self).__init__(url, connection_parameters) - - def get_parameters(self): - """Generic function to get all system parameters - - :returns: string -- parameter value - - """ - try: - return self.data - except: - return -1 - - 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 set_parameter(self, parameter_name, value): - """Generic function to set any bios 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() - action[parameter_name] = value - - # perform the POST action - response = self.api_url.patch(verify=self.connection_parameters.verify_cert, - headers={'x-auth-token': self.connection_parameters.auth_token}, - data=action - ) - return response class EthernetInterfacesCollection(BaseCollection): """Class to manage redfish EthernetInterfacesColkection data."""