diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 9b46494..f7e62ed 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,16 +1,5 @@ -If you would like to contribute to the development of OpenStack, -you must follow the steps in this page: +If you would like to contribute to the development of this project. - http://docs.openstack.org/infra/manual/developers.html +Submit your pull request and issues to https://github.com/bcornec/python-redfish. -Once those steps have been completed, changes to OpenStack -should be submitted for review via the Gerrit tool, following -the workflow documented at: - - http://docs.openstack.org/infra/manual/developers.html#development-workflow - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed on Launchpad, not GitHub: - - https://bugs.launchpad.net/python-redfish +You can also share and discuss on the mailing list as well. diff --git a/README.rst b/README.rst index 3491806..4b4cf50 100644 --- a/README.rst +++ b/README.rst @@ -20,14 +20,17 @@ Project Structure This project follows the same convention as OpenStack projects, eg. using pbr for build and test automation:: - doc/ # documentation - doc/source # the doc source files live here - doc/build/html # output of building any docs will go here + doc/ # Documentation + doc/source # The doc source files live here + doc/build/html # Output of building any docs will go here dmtf # Reference documents and mockup provided by the DMTF - examples/ # any sample code using this library, eg. for education + examples/ # Any sample code using this library, eg. for education # should be put here - redfish/ # the redfish library - redfish/tests/ # python unit test suite + pbconf # Project builder file to build rpm/deb packages for + # distributions + redfish/ # The redfish library itself + redfish/tests/ # Python redfish unit test suite + redfish-client # Client tool to manage redfish devices Requirements ------------ @@ -41,23 +44,31 @@ are discouraged due to security. Python requirements are listed in requirements.txt; additional requirements for running the unit test suite are listed in test-requirements.txt. -Developer setup +Get the sources --------------- -To initialize a local development environment (eg, so you can run unit tests) -you should run the following commands +The sources are available on github and can be retrieved using:: + + git clone https://github.com/uggla/python-redfish + +As python redefish is currently in heavy development we recommend to checkout the devel branch using:: + + cd python-redfish + git checkout devel + +Installation +------------ + +Please refer to the following link. + +http://pythonhosted.org/python-redfish/installation.html Contacts -------- -Distribution list : python-redfish@mondorescue.org +Distribution list: python-redfish@mondorescue.org Further References ------------------ -The data model documentation can be found here: - http://www.redfishspecification.org/redfish-data-model-and-schema/ - -The overall protocol documentation can be found here: - http://www.redfishspecification.org/ - +Please look at `dmtf/README.rst `_ file. diff --git a/dmtf/README.rst b/dmtf/README.rst index f113fb3..b44713b 100644 --- a/dmtf/README.rst +++ b/dmtf/README.rst @@ -1,12 +1,26 @@ DMTF Redfish specification -------------------------- -This directory contains the current references from the DMTF on the Redfish -specification (1.0.0 at the time of the writing) -In order to ease test, the DMTF has published a mockup environment to simulate -a Redfish based system so it is possible to write programs without real Redfish +This directory contains the current references from the DMTF on the Redfish +specification (1.0.1 at the time of the writing) + +The overall protocol documentation can be found online at: + http://www.dmtf.org/standards/redfish + +The specification can be found locally in DSP0266_1.0.1.pdf or online at: + http://www.dmtf.org/sites/default/files/standards/documents/DSP0266_1.0.1.pdf + +The data model documentation can be found locally in DSP8010_1.0.0.zip or online at: + http://redfish.dmtf.org/schemas/ + + +In order to ease test, the DMTF has published a mockup environment to simulate +a Redfish based system so it is possible to write programs without real Redfish compliant hardware platform. +Note: Mockup release is still 0.99.0a and so not aligned with specification realease +number. + Docker container ---------------- diff --git a/doc/source/conf.py b/doc/source/conf.py index c0e4fc1..30e45db 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -48,16 +48,16 @@ master_doc = 'index' # General information about the project. project = u'python-redfish' -copyright = u'2016, Bruno Cornec, Vincent Misson, René Ribaud' +copyright = u'2015-2016, Bruno Cornec, Vincent Misson, René Ribaud' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.4' +version = 'PBVER' # The full version, including alpha/beta/rc tags. -release = '0.4' +release = 'PBVER' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/develsetup.rst b/doc/source/develsetup.rst new file mode 100644 index 0000000..551a3fd --- /dev/null +++ b/doc/source/develsetup.rst @@ -0,0 +1,8 @@ +=============== +Developer setup +=============== + +#. Follow `get the sources `_ section to retrieve the sources. +#. Follow `using pip and virtualenv `_ section to create your environment. + +You can start hacking the code now. diff --git a/doc/source/further_ref.rst b/doc/source/further_ref.rst new file mode 100644 index 0000000..e0b2466 --- /dev/null +++ b/doc/source/further_ref.rst @@ -0,0 +1,3 @@ +:orphan: + +.. include:: ../../dmtf/README.rst diff --git a/doc/source/help.rst b/doc/source/help.rst new file mode 100644 index 0000000..e229349 --- /dev/null +++ b/doc/source/help.rst @@ -0,0 +1,12 @@ +============= +Help required +============= + +We need help on the following topic: + +- debian/ubuntu dependencies packaging. +- python3 testing. +- installation on distributions which are not Fedora or Mageia. +- documentation. + +Any contribution will be welcomed. diff --git a/doc/source/images/simulator.jpg b/doc/source/images/simulator.jpg new file mode 100644 index 0000000..6a47144 Binary files /dev/null and b/doc/source/images/simulator.jpg differ diff --git a/doc/source/index.rst b/doc/source/index.rst index af8520f..3897497 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -14,8 +14,10 @@ Contents: readme installation usage - contributing + develsetup classesdoc + contributing + help Indices and tables ================== diff --git a/doc/source/installation.rst b/doc/source/installation.rst index 41ac939..27dbfd4 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -2,11 +2,226 @@ Installation ============ -At the command line:: +Using pip +--------- +Use:: - $ pip install python-redfish + sudo pip install python-redfish -Or, if you have virtualenvwrapper installed:: +Pip will install : + +1. The library and all dependencies into your site-packages directory +2. Redfish client master conf file into /etc/redfish_client.conf +3. Data file (templates) into /usr/share/redfish-client/templates + +Point 2 and 3 above need root access to your system. If you don't have root +access on your system, please follow `Using pip and virtualenv`_ section. + + +Using pip and virtualenv +------------------------ + +1. Install virtualenv and virtualenvwrapper: + + Fedora 22:: + + dnf install python-virtualenv python-virtualenvwrapper + + Ubuntu 15.04:: + + apt-get install python-virtualenv virtualenvwrapper + +2. Source virtualenvwrapper.sh:: + + . /usr/bin/virtualenvwrapper.sh + + or:: + + . /usr/share/virtualenvwrapper/virtualenvwrapper.sh + +3. Create a redfish virtual environement:: + + mkvirtualenv redfish + +4. Install using pip:: + + pip install python-virtualenv + + All files are installed under your virtualenv. + +Using the sources +----------------- + +#. Follow `get the sources `_ section to retrieve the sources. +#. Install from the source using:: + + python setup.py install + + +Using rpm package +----------------- + +There is currently no oficial packages for distributions. +However part of the sources there is a mechanism to buil rpm or deb packages for distributions. + +The mechanism is based on `project builder `_ tool. + +#. Follow `get the sources `_ section to retrieve the sources. +#. Download project builder for your distribution from ftp://ftp.project-builder.org. +#. Clone the project to your own github account. +#. Create a .pbrc with the following content, replace "/wokspace/python/redfish" and "uggla" with your own directory and account:: + + [uggla@ugglalaptop ~]$ cat .pbrc + pbdefdir python-redfish = $ENV{'HOME'}/workspace + pbconfdir python-redfish = $ENV{'HOME'}/workspace/python-redfish/pbconf + pbconfurl python-redfish = git+ssh://git@github.com:uggla/python-redfish.git + pburl python-redfish = git+ssh://git@github.com:uggla/python-redfish.git +#. Build the project:: + + pb -p python-redfish sbx2pkg + + or:: + + pb -p python-redfish sbx2pkg2ins + +#. All packages (srpm/rpm) should be available into the build directory, then install the package using rpm:: + + rpm -Uvh python-redfish/build/RPMS/python-redfish-devel20160213182552.rpm + + +#. Follow `get the sources `_ section to retrieve the sources. + +Using deb package +----------------- + +This installation in not yet possible due to missing deb package dependencies. We are working on it. + +In the meantime we recommend to use `Using pip`_ or `Using pip and virtualenv`_. + +===================================== +Host configuration file configuration +===================================== + +#. Verify redfish-client is working correclty:: + + redfish-client.py -h + +#. Create a default entry to use the mockup:: + + redfish-client.py config add default default http://localhost:8000/redfish/v1 + +#. Verify the entry is correctly registered:: + + redfish-client.py config showall + +=================== +Mockup installation +=================== + +#. Follow `get the sources `_ section to retrieve the sources. +#. Install docker using your distribution packages or the docker `procedure `_ (docker provide more recent packages for ubuntu):: + + dnf install docker + systemctl enable docker.service + systemctl start docker.service + systemctl status docker.service + +#. Jump into the dmtf directory. +#. Run ./buildImage.sh and ./run-redfish-simulator.sh +#. Check that a container is running and listening on port 8000:: + + (pypi)[uggla@ugglalaptop dmtf]$ docker ps + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + 9943ff1d4d93 redfish-simulator:latest "/bin/sh -c /tmp/redf" 3 weeks ago Up 2 days 0.0.0.0:8000->80/tcp redfish-simulator +#. Try to connect using a navigator to http://localhost:8000 the following screen should apear. + + .. image:: images/simulator.jpg + +Note : in the above screenshot, firefox JSON-handle extension is used. If you want the same presentation install the extension and refresh the page. + +========================== +Testing against the mockup +========================== + +#. Follow `Host configuration file configuration`_ and `Mockup installation`_ section. +#. Run the following command:: + + redfish-client.py manager getinfo + +The result should be like this:: + + (pypi)[uggla@ugglalaptop dmtf]$ redfish-client.py manager getinfo + Gathering data from manager, please wait... + + Redfish API version : 1.00 + Root Service + + Managers information : + ====================== + + Manager id 1: + UUID : 00000000-0000-0000-0000-000000000000 + Type : BMC + Firmware version : 1.00 + State : Enabled + Ethernet Interface : + This manager has no ethernet interface + Managed Chassis : + 1 + Managed System : + 1 + ---------------------------- + + Manager id 2: + UUID : 00000000-0000-0000-0000-000000000000 + Type : EnclosureManager + Firmware version : Not available + State : Enabled + Ethernet Interface : + This manager has no ethernet interface + Managed Chassis : + Enc1 + Managed System : + 2 + ---------------------------- + + Manager id 3: + UUID : 00000000-0000-0000-0000-000000000000 + Type : EnclosureManager + Firmware version : Not available + State : Enabled + Ethernet Interface : + This manager has no ethernet interface + Managed Chassis : + Enc1 + Managed System : + 2 + ---------------------------- + + + +============================ +Building local documentation +============================ + +Building the html documentation locally + + +#. Follow `get the sources `_ section to retrieve the sources. +#. Jump in the doc directory:: + + cd doc +#. Build the html documentation:: + + make html + + +If you want to build the documentation in pdf. + +#. Get texlive full distribution, ex on Fedora:: + + dnf install texlive-scheme-full +#. Build the documentation:: + + make latexpdf - $ mkvirtualenv python-redfish - $ pip install python-redfish diff --git a/doc/source/usage.rst b/doc/source/usage.rst index e4b8cce..1c0e083 100644 --- a/doc/source/usage.rst +++ b/doc/source/usage.rst @@ -2,6 +2,25 @@ Usage ======== -To use python-redfish in a project:: +Example using the mockup +------------------------ + +example/simple-simulator.py provide a simple library usage to interact with the +redfish mockup. + +Example using a proliant +------------------------ + +example/simple-proliant.py provide a simple library usage to interact with a HP +proliant BL460C G9 server. However this example should work on any server supplier following redfish +standard. + +redfish-client usage +-------------------- + +The client usage can be display using:: + + redfish-client.py -h + +This is also available at http://pythonhosted.org/python-redfish/redfish-client.html . - import redfish diff --git a/examples/redfish.conf b/examples/redfish.conf index 80347c8..24cd951 100644 --- a/examples/redfish.conf +++ b/examples/redfish.conf @@ -1,5 +1,5 @@ { - "Nodes": { + "Managers": { "default": { "url": "", "login": "", diff --git a/examples/simple-proliant.py b/examples/simple-proliant.py index 468527b..451b7a1 100644 --- a/examples/simple-proliant.py +++ b/examples/simple-proliant.py @@ -43,48 +43,48 @@ except redfish.exception.RedfishException as e: 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_dict["1"].reset_system() # TODO : create an attribute to link the managed system directly -# and avoid systems_list[0] +# and avoid systems_dict["1"] # --> will be something like : -# remote_mgmt.Systems.systems_list[0] = remote_mgmt.Systems.managed_system +# remote_mgmt.Systems.systems_dict["1"] = 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 'SystemType' : {}\n".format(remote_mgmt.Systems.systems_list[0].get_parameter("SystemType"))) +print("Bios version : {}\n".format(remote_mgmt.Systems.systems_dict["1"].get_bios_version())) +print("Serial Number : {}\n".format(remote_mgmt.Systems.systems_dict["1"].get_serial_number())) +print("Power State : {}\n".format(remote_mgmt.Systems.systems_dict["1"].get_power())) +print("Parameter 'SystemType' : {}\n".format(remote_mgmt.Systems.systems_dict["1"].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 parameters : {}\n".format(remote_mgmt.Systems.systems_dict["1"].bios.get_parameters())) +print("Get boot parameters : {}\n".format(remote_mgmt.Systems.systems_dict["1"].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_dict["1"].bios.get_parameter("AdminPhone"))) +#print("Set bios parameter 'AdminPhone' to '' : {}\n".format(remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("AdminPhone",""))) #Boot server with script -#remote_mgmt.Systems.systems_list[0].bios.set_parameter("Dhcpv4","Enabled") +#remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("Dhcpv4","Enabled") -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_dict["1"].bios.set_parameter("PreBootNetwork", "Auto") +remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("UefiShellStartup", "Enabled") +remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("UefiShellStartupLocation", "NetworkLocation") +remote_mgmt.Systems.systems_dict["1"].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" : "Once"}}') +#remote_mgmt.Systems.systems_dict["1"].set_parameter_json('{"Boot": {"BootSourceOverrideTarget": "UefiShell"}}') +# remote_mgmt.Systems.systems_dict["1"].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Continuous"}}') +#remote_mgmt.Systems.systems_dict["1"].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Once"}}') -mySystem = remote_mgmt.Systems.systems_list[0] +mySystem = remote_mgmt.Systems.systems_dict["1"] 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())) +print("Get manager firmware version : {}\n".format(remote_mgmt.Managers.managers_dict["1"].get_firmware_version())) +print("Get system Bios version : {}\n".format(remote_mgmt.Systems.systems_dict["1"].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_dict["1"].reset_system() remote_mgmt.logout() diff --git a/examples/simple-simulator.py b/examples/simple-simulator.py index b9db1af..df44ca5 100644 --- a/examples/simple-simulator.py +++ b/examples/simple-simulator.py @@ -43,7 +43,7 @@ except redfish.exception.RedfishException as e: 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("Bios version : {}\n".format(remote_mgmt.Systems.systems_dict["1"].get_bios_version())) print("System 2 :\n") -print("Bios version : {}\n".format(remote_mgmt.Systems.systems_list[1].get_parameter("SerialNumber"))) +print("Bios version : {}\n".format(remote_mgmt.Systems.systems_dict["2"].get_parameter("SerialNumber"))) #print remoteMgmt.get_api_link_to_server() diff --git a/examples/walk-chassis.py b/examples/walk-chassis.py deleted file mode 100644 index 22781ba..0000000 --- a/examples/walk-chassis.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python - -#import logging -import sys -#from oslo_config import cfg -#from oslo_log import log as logging - -import redfish - -# Sets up basic logging for this module -#log_root = logging.getLogger('redfish') -#log_root.addHandler(logging.StreamHandler(sys.stdout)) -#log_root.setLevel(logging.DEBUG) - -#CONF = cfg.CONF -#logging.set_defaults(['redfish=DEBUG']) -#logging.register_options(CONF) -#logging.setup(CONF, "redfish") - -# Connect to a redfish API endpoint -host = 'http://localhost' -user_name = '' -password = '' - -# This returns a RedfishConnection object, which implements -# the low-level HTTP methods like GET, PUT, etc -connection = redfish.server.connect(host, user_name, password) - -# From this connection, we can get the Root resource. -# Note that the root resource is somewhat special - you create it from -# the connection, but you create other resources from the root resource. -# (You don't strictly have to do this, but it's simpler.) -root = connection.get_root() - -print("\n") -print("ROOT CONTROLLER") -print("===============") -print(root) - - -# The Root class has well-defined top-level resources, such as -# chassis, systems, managers, sessions, etc... -chassis = root.get_chassis() - -print("\n") -print("CHASSIS DATA") -print("============") -print(chassis) -print("\n") -print("WALKING CHASSIS") -print("\n") -print("CHASSIS contains %d items" % len(chassis)) -print("\n") -for item in chassis: - print("SYSTEM") - print("======") - print(item) - print("\n") diff --git a/pbconf/python-redfish.pb b/pbconf/python-redfish.pb index 42a1e60..6a8d804 100644 --- a/pbconf/python-redfish.pb +++ b/pbconf/python-redfish.pb @@ -130,7 +130,7 @@ defpkgdir python-redfish = . # List of files per pkg on which to apply filters # Files are mentioned relatively to pbroot/defpkgdir -filteredfiles python-redfish = redfish-client/redfish-client.py +filteredfiles python-redfish = redfish-client/redfish-client.py,doc/source/conf.py #supfiles python-redfish = python-redfish.init # For perl modules, names are different depending on distro diff --git a/redfish-client/etc/redfish-client.conf b/redfish-client/etc/redfish-client.conf new file mode 100644 index 0000000..89cd47b --- /dev/null +++ b/redfish-client/etc/redfish-client.conf @@ -0,0 +1,2 @@ +[redfish-client] +templates_path = /usr/share/redfish-client/templates diff --git a/redfish-client/redfish-client.py b/redfish-client/redfish-client.py index 25ed9fc..d7a509e 100755 --- a/redfish-client/redfish-client.py +++ b/redfish-client/redfish-client.py @@ -37,10 +37,10 @@ import json import pprint import docopt import logging -import redfish -import requests.packages.urllib3 +import ConfigParser import jinja2 - +import requests.packages.urllib3 +import redfish class ConfigFile(object): '''redfisht-client configuration file management''' @@ -253,7 +253,13 @@ if __name__ == '__main__': sys.exit(1) # Display manager information using jinja2 template - template = jinja2_env.get_template("manager_info.template") + try: + template = jinja2_env.get_template("manager_info.template") + except jinja2.exceptions.TemplateNotFound as e: + print('Template "{}" not found in {}.'.format(e.message, jinja2_env.loader.searchpath[0])) + logger.debug('Template "%s" not found in %s.' % (e.message, jinja2_env.loader.searchpath[0])) + sys.exit(1) + print template.render(r=remote_mgmt) @@ -314,23 +320,48 @@ if __name__ == '__main__': logger.info("Arguments parsed") logger.debug(arguments) - # Get $HOME environment. + # Get $HOME and $VIRTUAL_ENV environment variables. HOME = os.getenv('HOME') + VIRTUAL_ENV = os.getenv('VIRTUAL_ENV') if not HOME: print('$HOME environment variable not set, please check your system') logger.error('$HOME environment variable not set') sys.exit(1) logger.debug("Home directory : %s" % HOME) + + if VIRTUAL_ENV: + logger.debug("Virtual env : %s" % VIRTUAL_ENV) + + # Load master conf file + config = ConfigParser.ConfigParser(allow_no_value=True) + logger.debug("Read master configuration file") + master_conf_file_path = "/etc/redfish-client.conf" + + if VIRTUAL_ENV: + logger.debug("Read master configuration file from virtual environment") + master_conf_file_path = VIRTUAL_ENV + master_conf_file_path + + if not os.path.isfile(master_conf_file_path): + print('Master configuration file not found at {}.'.format(master_conf_file_path)) + logger.error('Master configuration file not found at %s.' % master_conf_file_path) + sys.exit(1) + + config.read(master_conf_file_path) arguments['--conf_file'] = arguments['--conf_file'].replace('~', HOME) conf_file = ConfigFile(arguments['--conf_file']) # Initialize Template system (jinja2) # TODO : set the template file location into cmd line default to /usr/share/python-redfish/templates ? + templates_path = config.get("redfish-client", "templates_path") logger.debug("Initialize template system") - jinja2_env = jinja2.Environment(loader=jinja2.FileSystemLoader("templates")) - + if VIRTUAL_ENV: + logger.debug("Read templates file from virtual environment") + templates_path = VIRTUAL_ENV + templates_path + jinja2_env = jinja2.Environment(loader=jinja2.FileSystemLoader(templates_path)) + + # Check cmd line parameters if arguments['config'] is True: logger.debug("Config commands") if arguments['show'] is True: diff --git a/redfish-client/templates/bla.templates b/redfish-client/templates/bla.templates deleted file mode 100644 index 23b6b61..0000000 --- a/redfish-client/templates/bla.templates +++ /dev/null @@ -1,32 +0,0 @@ - #======================================================================= - # print('Redfish API version : %s' % remote_mgmt.get_api_version()) - # print(remote_mgmt.Root.get_name()) - # print('\n') - # print('Managers information :') - # print('----------------------') - # for manager_index in sorted(remote_mgmt.Managers.managers_dict): - # manager = remote_mgmt.Managers.managers_dict[manager_index] - # print('\nManager id {} :').format(manager_index) - # print('UUID : {}').format(manager.get_uuid()) - # print('Type : {}').format(manager.get_type()) - # print('Firmware version : {}').format(manager.get_firmware_version()) - # print('State : {}').format(manager.get_status()) - # print manager.get_managed_chassis() - # print manager.get_managed_systems() - # print('Ethernet interfaces :') - # try : - # for ethernetinterface_index in sorted(manager.ethernet_interfaces_collection.ethernet_interfaces_dict): - # ei = manager.ethernet_interfaces_collection.ethernet_interfaces_dict[ethernetinterface_index] - # print('\nEthernet Interface id {} :').format(ethernetinterface_index) - # print(ei.get_name()) - # print(ei.get_parameter('FQDN')) - # print ei.get_ipv4() - # print ei.get_ipv6() - # except AttributeError: - # # We don't have ethernet interfaces - # pass - #======================================================================= - - -Redfish API version : remote_mgmt.get_api_version() -remote_mgmt.Root.get_name() \ No newline at end of file diff --git a/redfish-client/templates/manager_info.template b/redfish-client/templates/manager_info.template index 71961ba..d3f0d22 100644 --- a/redfish-client/templates/manager_info.template +++ b/redfish-client/templates/manager_info.template @@ -1,4 +1,4 @@ -Redfish API version : {{ r.get_api_version() }} +Redfish API version : {{ r.get_api_version() }} {{ r.Root.get_name() }} Managers information : @@ -17,11 +17,12 @@ Ethernet Interface : Ethernet Interface id {{ ethernetinterface_index }} : {{ ei.get_name() }} FQDN : {{ ei.get_fqdn() }} + Mac address : {{ ei.get_mac() }} Address ipv4 : {{ ei.get_ipv4() | join(', ') }} Address ipv6 : {{ ei.get_ipv6() | join(', ') }} {%- endfor %} {%- else %} - This manager has no ethernet interface + This manager has no ethernet interface {%- endif %} Managed Chassis : {{ manager.get_managed_chassis() | join(', ') }} diff --git a/redfish/__init__.py b/redfish/__init__.py index b1e8444..ce09d8f 100644 --- a/redfish/__init__.py +++ b/redfish/__init__.py @@ -12,11 +12,15 @@ # License for the specific language governing permissions and limitations # under the License. -#import pbr.version +import pbr.version from redfish.main import * #import redfish.types - -#__version__ = pbr.version.VersionInfo( -# 'redfish').version_string() +try: + __version__ = pbr.version.VersionInfo('redfish').version_string() +except Exception, e: + if "Versioning for this project requires either an sdist tarball" in e.message: + pass + else: + raise diff --git a/redfish/exception.py b/redfish/exception.py index 5152f80..d54feff 100644 --- a/redfish/exception.py +++ b/redfish/exception.py @@ -15,8 +15,13 @@ class RedfishException(Exception): 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' + self.advices = \ + '1- Check if the url is the correct one\n' + \ + '2- Check if your device is answering on the network\n' + \ + '3- 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 :443\n' + \ + '4- Use option "--insecure" to connect without checking certificate\n' class InvalidRedfishContentException(RedfishException): diff --git a/redfish/main.py b/redfish/main.py index 3a28cf0..98c0205 100644 --- a/redfish/main.py +++ b/redfish/main.py @@ -247,8 +247,6 @@ class RedfishConnection(object): self.connection_parameters ) - #for system in self.Systems.systems_list: - #config.logger.debug(system.data.links.ManagedBy) # self.Chassis # self.EventService diff --git a/redfish/old/functions.py b/redfish/old/functions.py deleted file mode 100644 index 1de1031..0000000 --- a/redfish/old/functions.py +++ /dev/null @@ -1,49 +0,0 @@ - - # Copyright 2014 Hewlett-Packard Development Company, L.P. - # - # 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. - - -""" - -Provides functions for using the Redfish RESTful API. - -""" - -import collections -import json -import sys -from redfish import connection - -class RedfishOperation(connection.RedfishConnection): - - def reset_server(self): - (status, headers, system) = self.rest_get('/redfish/v1/Systems', None) - - memberuri = system['links']['Member'][0]['href'] - # verify expected type - # hint: don't limit to version 0 here as we will rev to 1.0 at some point hopefully with minimal changes - # assert(connection.get_type(system) == 'ComputerSystem.0' or connection.get_type(system) == 'ComputerSystem.1') - - # verify it supports POST - # assert(connection.operation_allowed(headers, 'POST')) - - action = dict() - action['Action'] = 'Reset' - action['ResetType'] = 'ForceRestart' - - # perform the POST action - print('POST ' + json.dumps(action) + ' to ' + memberuri) - (status, headers, response) = self.rest_post(memberuri, None, action) - print('POST response = ' + str(status)) - connection.print_extended_error(response) diff --git a/redfish/old/types.py b/redfish/old/types.py deleted file mode 100644 index ede311f..0000000 --- a/redfish/old/types.py +++ /dev/null @@ -1,197 +0,0 @@ -# Copyright 2014 Hewlett-Packard Development Company, L.P. -# -# 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. - - -""" -Redfish Resource Types -""" - -import base64 -import gzip -import hashlib -import httplib -import json -import ssl -import StringIO -import sys -import urllib2 -from urlparse import urlparse - -#from oslo_log import log as logging -from redfish import exception - -#LOG = logging.getLogger('redfish') - - -class Base(object): - def __init__(self, obj, connection=None): - self._conn = connection - """handle to the redfish connection""" - - self._attrs = [] - """list of discovered attributes""" - - self._links = [] - """list of linked resources""" - - # parse the individual resources, appending them to - # the list of object attributes - for k in obj.keys(): - ref = k.lower() - if ref in ["links", "oem", "items"]: - continue - setattr(self, ref, obj[k]) - self._attrs.append(ref) - - # make sure the required attributes are present - if not getattr(self, 'name', False): - raise ObjectLoadException( - "Failed to load object. Reason: could not determine name.") - if not getattr(self, 'type', False): - raise ObjectLoadException( - "Failed to load object. Reason: could not determine type.") - - if getattr(self, 'serviceversion', False): - self.type = self.type.replace('.' + self.serviceversion, '') - else: - # TODO: use a regex here to strip and store the version - # instead of assuming it is 7 chars long - self.type = self.type[:-7] - - # Lastly, parse the 'links' resource. - # Note that this may have different nested structure, depending on - # what type of resource this is, or what vendor it is. - # subclasses may follow this by parsing other resources / collections - self._parse_links(obj) - - def _parse_links(self, obj): - """Map linked resources to getter functions - - The root resource returns a dict of links to top-level resources - """ - def getter(connection, href): - def _get(): - return connection.rest_get(href, {}) - return _get - - for k in obj['links']: - ref = "get_" + k.lower() - self._links.append(ref) - href = obj['links'][k]['href'] - setattr(self, ref, getter(self._conn, href)) - - def __repr__(self): - """Return this object's _attrs as a dict""" - res = {} - for a in self._attrs: - res[a] = getattr(self, a) - return res - - def __str__(self): - """Return the string representation of this object's _attrs""" - return json.dumps(self.__repr__()) - - -class BaseCollection(Base): - """Base class for collection types""" - def __init__(self, obj, connection=None): - super(BaseCollection, self).__init__(obj, connection=connection) - self._parse_items(obj) - self._attrs.append('items') - - def _parse_links(self, obj): - """links are special on a chassis; dont parse them""" - pass - - def _parse_items(self, obj): - """Map linked items to getter methods - - The chassis resource returns a list of items and corresponding - link data in a separate entity. - """ - def getter(connection, href): - def _get(): - return connection.rest_get(href, {}) - return _get - - self.items = [] - self._item_getters = [] - - if 'links' in obj and 'Member' in obj['links']: - # NOTE: this assumes the lists are ordered the same - counter = 0 - for item in obj['links']['Member']: - self.items.append(obj['Items'][counter]) - self._item_getters.append( - getter(self._conn, item['href'])) - counter+=1 - elif 'Items' in obj: - # TODO: find an example of this format and make sure it works - for item in obj['Items']: - if 'links' in item and 'self' in item['links']: - href = item['links']['self']['href'] - self.items.append(item) - - # TODO: implement paging support - # if 'links' in obj and 'NextPage' in obj['links']: - # next_page = THIS_URI + '?page=' + str(obj['links']['NextPage']['page']) - # do something with next_page URI - - def __iter__(self): - for getter in self._item_getters: - yield getter() - - -class Root(Base): - """Root '/' resource class""" - def _parse_links(self, obj): - """Map linked resources to getter functions - - The root resource returns a dict of links to top-level resources - - TODO: continue implementing customizations for top-level resources - - """ - mapping = { - 'Systems': Systems, - 'Chassis': Chassis, - 'Managers': Base, - 'Schemas': Base, - 'Registries': Base, - 'Tasks': Base, - 'AccountService': Base, - 'Sessions': Base, - 'EventService': Base, - } - - def getter(connection, href, type): - def _get(): - return mapping[type](connection.rest_get(href, {}), self._conn) - return _get - - for k in obj['links']: - ref = "get_" + k.lower() - self._links.append(ref) - href = obj['links'][k]['href'] - setattr(self, ref, getter(self._conn, href, k)) - - -class Chassis(BaseCollection): - """Chassis resource class""" - def __len__(self): - return len(self.items) - - -class Systems(Base): - pass diff --git a/redfish/types.py b/redfish/types.py index ecdd477..babc5d0 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -23,7 +23,7 @@ class Base(object): self.api_url = tortilla.wrap(url, debug=config.TORTILLADEBUG) try: - if connection_parameters.auth_token == None: + if connection_parameters.auth_token is None: self.data = self.api_url.get(verify=connection_parameters.verify_cert) else: self.data = self.api_url.get(verify=connection_parameters.verify_cert, @@ -63,11 +63,11 @@ class Base(object): if float(mapping.redfish_version) < 1.00: links = getattr(self.data, mapping.redfish_mapper.map_links()) if link_type in links: - return urljoin(self.url, links[link_type][mapping.redfish_mapper.map_links_ref()]) + return urljoin(self.url, links[link_type][mapping.redfish_mapper.map_links_ref()]) else: links = getattr(self.data, link_type) link = getattr(links, mapping.redfish_mapper.map_links_ref()) - return urljoin(self.url, link) + return urljoin(self.url, link) @property def url(self): @@ -120,7 +120,7 @@ class Base(object): headers={'x-auth-token': self.connection_parameters.auth_token}, data=action) return response - + def get_name(self): '''Get root name @@ -138,8 +138,7 @@ class BaseCollection(Base): def __init__(self, url, connection_parameters): super(BaseCollection, self).__init__(url, connection_parameters) - self.links=[] - + self.links = [] #linksmembers = self.data.Links.Members #linksmembers = self.data.links.Member @@ -153,7 +152,6 @@ class BaseCollection(Base): #self.links.append(getattr(link,'href')) self.links.append(urljoin(self.url, getattr(link, mapping.redfish_mapper.map_links_ref()))) - config.logger.debug(self.links) @@ -189,7 +187,7 @@ class Root(Base): :returns: string -- path ''' - return getattr(self.root.Links.Systems, '@odata.id') + return getattr(self.root.Links.Systems, '@odata.id') class SessionService(Base): @@ -204,10 +202,10 @@ class Managers(Base): try: # New proliant firmware now respects Redfish v1.00, so seems to correct below statement # TODO : better handle exception and if possible support old firmware ? - self.ethernet_interfaces_collection = EthernetInterfacesCollection( - self.get_link_url('EthernetInterfaces'), - connection_parameters - ) + self.ethernet_interfaces_collection = \ + EthernetInterfacesCollection( + self.get_link_url('EthernetInterfaces'), + connection_parameters) # Works on proliant, need to treat 095 vs 0.96 differences #self.ethernet_interfaces_collection = EthernetInterfacesCollection( @@ -217,11 +215,10 @@ class Managers(Base): except exception.InvalidRedfishContentException: # This is to avoid invalid content from the mockup self.ethernet_interfaces_collection = None - + except AttributeError: # This means we don't have EthernetInterfaces self.ethernet_interfaces_collection = None - def get_firmware_version(self): '''Get firmware version of the manager @@ -257,7 +254,7 @@ class Managers(Base): return self.data.UUID except AttributeError: return "Not available" - + def get_status(self): '''Get manager status @@ -277,7 +274,7 @@ class Managers(Base): ''' chassis_list = [] links = getattr(self.data, mapping.redfish_mapper.map_links(self.data)) - + try: for chassis in links.ManagerForChassis: result = re.search(r'Chassis/(\w+)', chassis[mapping.redfish_mapper.map_links_ref(chassis)]) @@ -294,15 +291,36 @@ class Managers(Base): ''' systems_list = [] links = getattr(self.data, mapping.redfish_mapper.map_links(self.data)) - + try: for systems in links.ManagerForServers: result = re.search(r'Systems/(\w+)', systems[mapping.redfish_mapper.map_links_ref(systems)]) - systems_list.append(result.group(1)) + systems_list.append(result.group(1)) return systems_list except AttributeError: return "Not available" + def reset(self): + '''Reset the manager. + + :returns: string -- http response of POST request + + ''' + # Craft the request + link = getattr(self.data.Actions, "#Manager.Reset") + link = link.target + + reset_url = urljoin(self.url, link) + + response = requests.post( + reset_url, + verify=self.connection_parameters.verify_cert, + headers={'x-auth-token': self.connection_parameters.auth_token, + 'Content-type': 'application/json'}) + # TODO : treat response. + return response + + class ManagersCollection(BaseCollection): '''Class to manage redfish ManagersCollection data.''' def __init__(self, url, connection_parameters): @@ -344,7 +362,7 @@ class Systems(Base): data=action ) #TODO : treat response. - return response + return response def get_bios_version(self): '''Get bios version of the system. @@ -429,10 +447,11 @@ class SystemsCollection(BaseCollection): def __init__(self, url, connection_parameters): super(SystemsCollection, self).__init__(url, connection_parameters) - self.systems_list = [] + self.systems_dict = {} for link in self.links: - self.systems_list.append(Systems(link, connection_parameters)) + index = re.search(r'Systems/(\w+)', link) + self.systems_dict[index.group(1)] = Systems(link, connection_parameters) class Bios(Base): @@ -451,7 +470,8 @@ class Boot(Base): class EthernetInterfacesCollection(BaseCollection): '''Class to manage redfish EthernetInterfacesColkection data.''' def __init__(self, url, connection_parameters): - super(EthernetInterfacesCollection, self).__init__(url, connection_parameters) + super(EthernetInterfacesCollection, + self).__init__(url, connection_parameters) self.ethernet_interfaces_dict = {} @@ -460,7 +480,8 @@ class EthernetInterfacesCollection(BaseCollection): # Check more than 1 hour for this bug.... grrr.... for link in self.links: index = re.search(r'EthernetInterfaces/(\w+)', link) - self.ethernet_interfaces_dict[index.group(1)] = EthernetInterfaces(link, connection_parameters) + self.ethernet_interfaces_dict[index.group(1)] = \ + EthernetInterfaces(link, connection_parameters) class EthernetInterfaces(Base): @@ -486,40 +507,39 @@ class EthernetInterfaces(Base): return self.data.FQDN except AttributeError: return "Not available" - def get_ipv4(self): '''Get EthernetInterface ipv4 address - + :returns: list -- interface ip addresses or "Not available" - + ''' - + ipaddresses = [] - + try: for ip_settings in self.data.IPv4Addresses: address = ip_settings['Address'] ipaddresses.append(address) - + return ipaddresses except AttributeError: return "Not available" def get_ipv6(self): '''Get EthernetInterface ipv6 address - + :returns: list -- interface ip addresses or "Not available" - + ''' - + ipaddresses = [] - + try: for ip_settings in self.data.IPv6Addresses: address = ip_settings['Address'] ipaddresses.append(address) - + return ipaddresses except AttributeError: return "Not available" diff --git a/requirements.txt b/requirements.txt index a6fc6e4..6eecbba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,5 @@ Babel>=1.3 tortilla>=0.4.1 Jinja2>=2.7.3 Sphinx>=1.2.3 +docopt>=0.6.2 +simplejson>=3.8.1 diff --git a/setup.cfg b/setup.cfg index 0482d17..162a4f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,24 +5,32 @@ description-file = README.rst author = Redfish dev team author-email = python-redfish@mondorescue.org -home-page = http://github.com/bcornec/python-redfish +home-page = https://github.com/uggla/python-redfish/tree/devel +license = Apache-2 classifier = - #Environment :: OpenStack - Intended Audience :: Information Technology - Intended Audience :: System Administrators - License :: OSI Approved :: Apache Software License - Operating System :: POSIX :: Linux - Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 2.6 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.3 - Programming Language :: Python :: 3.4 + Development Status :: 4 - Beta + Environment :: Console + Intended Audience :: Information Technology + Intended Audience :: System Administrators + License :: OSI Approved :: Apache Software License + Operating System :: POSIX :: Linux + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.4 [files] packages = - redfish + redfish + +scripts = + redfish-client/redfish-client.py + +data_files = + usr/share/redfish-client/templates = redfish-client/templates/* + etc/ = redfish-client/etc/* + [build_sphinx] source-dir = doc/source