├── .coveragerc ├── .gitignore ├── .gitreview ├── .mailmap ├── .stestr.conf ├── CONTRIBUTING.rst ├── HACKING.rst ├── LICENSE ├── README.rst ├── bindep.txt ├── doc ├── requirements.txt └── source │ ├── admin │ ├── emulator.conf │ └── index.rst │ ├── conf.py │ ├── contributor │ └── index.rst │ ├── index.rst │ ├── install │ └── index.rst │ └── user │ ├── dynamic-emulator.rst │ ├── index.rst │ └── static-emulator.rst ├── pyproject.toml ├── releasenotes ├── notes │ ├── .placeholder │ ├── add-chassis-resource-aab57e319e6c7088.yaml │ ├── add-drive-resource-a827e136600071f3.yaml │ ├── add-http-boot-uri-handling-c678bd89c7f6346c.yaml │ ├── add-http-to-vmedia-ef04c156e66fc121.yaml │ ├── add-indicator-led-resource-672255319e6c3421.yaml │ ├── add-ironic-6f446bf16276b4dd.yaml │ ├── add-libvirt-firware-autoselect-b743737cb1cf9c5e.yaml │ ├── add-managers-resource-ffa58e329eccc058.yaml │ ├── add-processor-schema-f23195a923f5830f.yaml │ ├── add-simple-storage-resource-200e78d78c6aa8df.yaml │ ├── add-storage-resource-52bf30f50dd7b0a1.yaml │ ├── add-thermal-resource-8623543795643123.yaml │ ├── add-updateservice-3332a9c15b5fb3ab.yaml │ ├── add-virtual-media-4a137a5fb5017031.yaml │ ├── add-volume-collection-to-storage-template-b673a7acfbb1a03e.yaml │ ├── add-volume-resource-db795af928e41e5c.yaml │ ├── add_message_and_attribute_registries-71e14a53bfa658da.yaml │ ├── auth-044dab149ab0c03f.yaml │ ├── auth-config-4f05bbfaea15bd2b.yaml │ ├── auth-config-7705910e241909c1.yaml │ ├── bios-integer-types-79928fbd0b49ac57.yaml │ ├── certificate-service-ff8061143d454313.yaml │ ├── debug-mode-0dccdc3702c0590e.yaml │ ├── drop-python2-48e8388f96998244.yaml │ ├── fakedriver-819d46b6f1e18081.yaml │ ├── feature-set-42f2846a17f59424.yaml │ ├── fix-bios-registry-handler-63528fb2e114a04c.yaml │ ├── fix-boot-mode-constant-71e6a7ec6b010273.yaml │ ├── fix-libvirt-boot-image-965d94fd30bca7b0.yaml │ ├── fix-libvirt-boot-mode-3ec2ccf49f96aec8.yaml │ ├── fix-libvirt-list-1ccf5a18bc89793d.yaml │ ├── fix-libvirt-statefulness-0a2a7812d79fdd25.yaml │ ├── fix-redirect-code-a0aa43251be7de0c.yaml │ ├── fix-retain-boot-device-5ae02731d2a000d3.yaml │ ├── fix-set-boot-mode-forbidden-085efb2aea6987ff.yaml │ ├── fix-storage-drive-defaults-3a925a0063738b77.yaml │ ├── fix-wsgi-config-e3f63f91152225f8.yaml │ ├── floppy-1be04ed78708caf1.yaml │ ├── http-code-b4e32ad8485ef841.yaml │ ├── no-boot-1709a77ecc140034.yaml │ ├── novadriver-reduce-api-calls-934eb2af95ea2419.yaml │ ├── preserve-libvirt-domain-info-955410f570060241.yaml │ ├── py36-37-bye-bye-5a0cfafa9cfe8230.yaml │ ├── q35-aea1a63ff2c7c72b.yaml │ ├── remove-py38-ec073d8462e60e8b.yaml │ ├── secure-boot-de663109ced9b266.yaml │ ├── unsupported-http-status-code-05af22dbecc517f7.yaml │ ├── updatable-firmware-versions-b1f947339b2e6b94.yaml │ ├── updateservice-redirect-workaround-97c4864ef20a2eef.yaml │ ├── verify-certificate-798f84905cee03e5.yaml │ ├── virtual-media-alternate-bus-6d984ae45acb7ac4.yaml │ ├── vmedia-credentials-0f6e1f539bd94d14.yaml │ ├── vmedia-error-73fda32cf6046554.yaml │ ├── vmedia-openstack-fc422b845c343fc3.yaml │ ├── vmedia-openstack-file-upload-8a6843d6d0b33772.yaml │ └── vmedia-system-c254f4d0918d8b91.yaml └── source │ ├── _static │ └── .placeholder │ ├── _templates │ └── .placeholder │ ├── conf.py │ ├── index.rst │ └── unreleased.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── sushy_tools ├── __init__.py ├── emulator │ ├── __init__.py │ ├── api_utils.py │ ├── auth_basic.py │ ├── base.py │ ├── constants.py │ ├── controllers │ │ ├── __init__.py │ │ ├── certificate_service.py │ │ ├── update_service.py │ │ └── virtual_media.py │ ├── main.py │ ├── memoize.py │ ├── resources │ │ ├── __init__.py │ │ ├── base.py │ │ ├── chassis.py │ │ ├── drives.py │ │ ├── indicators.py │ │ ├── managers.py │ │ ├── storage.py │ │ ├── systems │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── fakedriver.py │ │ │ ├── ironicdriver.py │ │ │ ├── libvirtdriver.py │ │ │ └── novadriver.py │ │ ├── vmedia.py │ │ └── volumes.py │ └── templates │ │ ├── bios.json │ │ ├── bios_attribute_registry_file.json │ │ ├── bios_registry.json │ │ ├── bios_settings.json │ │ ├── certificate.json │ │ ├── certificate_collection.json │ │ ├── certificate_locations.json │ │ ├── certificate_service.json │ │ ├── chassis.json │ │ ├── chassis_collection.json │ │ ├── drive.json │ │ ├── error.json │ │ ├── ethernet_interface.json │ │ ├── ethernet_interfaces_collection.json │ │ ├── manager_collection.json │ │ ├── message_registry.json │ │ ├── message_registry_file.json │ │ ├── processor.json │ │ ├── processors_collection.json │ │ ├── registry_file_collection.json │ │ ├── root.json │ │ ├── secure_boot.json │ │ ├── simple_storage.json │ │ ├── simple_storage_collection.json │ │ ├── storage.json │ │ ├── storage_collection.json │ │ ├── system.json │ │ ├── system_collection.json │ │ ├── task.json │ │ ├── task_service.json │ │ ├── thermal.json │ │ ├── update_service.json │ │ ├── virtual_media.json │ │ ├── virtual_media_collection.json │ │ ├── volume.json │ │ └── volume_collection.json ├── error.py ├── static │ ├── __init__.py │ └── main.py └── tests │ ├── __init__.py │ └── unit │ ├── __init__.py │ ├── base.py │ └── emulator │ ├── __init__.py │ ├── controllers │ ├── __init__.py │ ├── test_certificate_service.py │ └── test_virtual_media.py │ ├── domain-q35.xml │ ├── domain-q35_fw_auto_uefi.xml │ ├── domain-q35_fw_auto_uefi_secure.xml │ ├── domain-q35_uefi.xml │ ├── domain-q35_uefi_secure.xml │ ├── domain-sata.xml │ ├── domain-scsi.xml │ ├── domain.xml │ ├── domain_bios.xml │ ├── domain_boot_disk.xml │ ├── domain_boot_network.xml │ ├── domain_boot_os.xml │ ├── domain_fw_auto.xml │ ├── domain_metadata.xml │ ├── domain_nics.xml │ ├── domain_processors.xml │ ├── domain_processors_notopology.xml │ ├── domain_simple_storage.xml │ ├── domain_to_boot_pxe.xml │ ├── domain_versions.xml │ ├── pool.xml │ ├── resources │ ├── __init__.py │ ├── systems │ │ ├── __init__.py │ │ ├── test_fakedriver.py │ │ ├── test_ironic.py │ │ ├── test_libvirt.py │ │ └── test_nova.py │ ├── test_chassis.py │ ├── test_drives.py │ ├── test_indicators.py │ ├── test_managers.py │ ├── test_storage.py │ ├── test_update_service.py │ ├── test_vmedia.py │ └── test_volumes.py │ ├── test_api_utils.py │ ├── test_auth_basic.py │ ├── test_main.py │ └── test_memoize.py ├── test-requirements.txt ├── tox.ini └── zuul.d ├── project.yaml └── sushy-tools-jobs.yaml /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | source = sushy-tools 4 | 5 | [report] 6 | ignore_errors = True 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg* 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | cover/ 26 | .coverage* 27 | !.coveragerc 28 | .tox 29 | nosetests.xml 30 | .stestr/ 31 | .venv 32 | 33 | # Translations 34 | *.mo 35 | 36 | # Mr Developer 37 | .mr.developer.cfg 38 | .project 39 | .pydevproject 40 | 41 | # Complexity 42 | output/*.html 43 | output/*/index.html 44 | 45 | # Sphinx 46 | doc/build 47 | 48 | # pbr generates these 49 | AUTHORS 50 | ChangeLog 51 | 52 | # Editors 53 | *~ 54 | .*.swp 55 | .*sw? 56 | 57 | # Files created by releasenotes build 58 | releasenotes/build 59 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.opendev.org 3 | port=29418 4 | project=openstack/sushy-tools.git 5 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | # Format is: 2 | # 3 | # 4 | -------------------------------------------------------------------------------- /.stestr.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | test_path=./sushy_tools/tests 3 | top_dir=. 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | If you would like to contribute to the development of OpenStack, you must 2 | follow the steps in this page: 3 | 4 | http://docs.openstack.org/infra/manual/developers.html 5 | 6 | If you already have a good understanding of how the system works and your 7 | OpenStack accounts are set up, you can skip to the development workflow 8 | section of this documentation to learn how changes to OpenStack should be 9 | submitted for review via the Gerrit tool: 10 | 11 | http://docs.openstack.org/infra/manual/developers.html#development-workflow 12 | 13 | Pull requests submitted through GitHub will be ignored. 14 | 15 | Bugs should be filed on Launchpad, not GitHub: 16 | 17 | https://bugs.launchpad.net/sushy 18 | -------------------------------------------------------------------------------- /HACKING.rst: -------------------------------------------------------------------------------- 1 | Sushy Tools Style Commandments 2 | ============================== 3 | 4 | Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ 5 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Redfish development tools 3 | ========================= 4 | 5 | This is a set of simple simulation tools aimed at supporting the development and 6 | testing of the Redfish protocol implementations and, in particular, the Sushy 7 | library (https://docs.openstack.org/sushy/). It is not designed for use outside 8 | of development and testing environments. Please do not run sushy-tools in a 9 | production environment of any kind. 10 | 11 | The package ships two simulators - the static Redfish responder and the virtual 12 | Redfish BMC (which is backed by libvirt or OpenStack cloud). 13 | 14 | The static Redfish responder is a simple REST API server which responds with the 15 | same things to client queries. It is effectively read-only. 16 | 17 | The virtual Redfish BMC resembles the real Redfish-controlled bare metal machine 18 | to some extent. Some client queries are translated to commands that actually 19 | control VM instances simulating bare metal hardware. However, some of the 20 | Redfish commands just return static content, never touching the virtualization 21 | backend and in this regard, the virtual Redfish BMC is similar to the static 22 | Redfish responder. 23 | 24 | * Free software: Apache license 25 | * Documentation: https://docs.openstack.org/sushy-tools 26 | * Source: http://opendev.org/openstack/sushy-tools 27 | * Bugs: https://storyboard.openstack.org/#!/project/openstack/sushy-tools 28 | -------------------------------------------------------------------------------- /bindep.txt: -------------------------------------------------------------------------------- 1 | libvirt-dev [platform:dpkg] 2 | libvirt-devel [platform:rpm] 3 | pkg-config [platform:dpkg] 4 | # fonts-freefont-otf is needed for pdf docs builds with the 'xelatex' engine 5 | fonts-freefont-otf [doc] 6 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | reno>=3.1.0 # Apache-2.0 2 | sphinx>=2.0.0,!=2.1.0 # BSD 3 | openstackdocstheme>=2.2.1 # Apache-2.0 4 | -------------------------------------------------------------------------------- /doc/source/admin/index.rst: -------------------------------------------------------------------------------- 1 | 2 | Configuring emulators 3 | ===================== 4 | 5 | Running emulators in the background 6 | ----------------------------------- 7 | 8 | The emulators run as interactive processes attached to the terminal by default. 9 | You can create systemd services to run the emulators in the background. 10 | For each emulator, create a systemd unit file, and update ```` to the 11 | ``sushy-static`` or ``sushy-emulator`` binary, and adjust the arguments as 12 | necessary, for example:: 13 | 14 | [Unit] 15 | Description=Sushy Libvirt emulator 16 | After=syslog.target 17 | 18 | [Service] 19 | Type=simple 20 | ExecStart=//sushy-emulator --port 8000 --libvirt-uri "qemu:///system" 21 | StandardOutput=syslog 22 | StandardError=syslog 23 | 24 | If you want to run the emulators with different configurations, for example, the 25 | ``sushy-static`` emulator with different mockup files, then create a new systemd 26 | unit file. 27 | 28 | You can also use ``gunicorn`` to run ``sushy-emulator``, for example:: 29 | 30 | ExecStart=/usr/bin/gunicorn sushy_tools.emulator.main:app 31 | 32 | Using configuration file 33 | ------------------------ 34 | 35 | Besides command-line options, `sushy-emulator` can be configured via a 36 | configuration file. This tool uses the Flask application 37 | `configuration infrastructure `_. 38 | Emulator-specific configuration options are prefixed with **SUSHY_EMULATOR_** 39 | to make sure that they don't collide with Flask's own configuration options. 40 | 41 | The configuration file itself can be specified through the 42 | `SUSHY_EMULATOR_CONFIG` environment variable. 43 | 44 | The full list of supported options and their meanings can be found in the sample 45 | configuration file: 46 | 47 | .. literalinclude:: emulator.conf 48 | -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 11 | # implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import sys 17 | 18 | sys.path.insert(0, os.path.abspath('../..')) 19 | # -- General configuration ---------------------------------------------------- 20 | 21 | # Add any Sphinx extension module names here, as strings. They can be 22 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 23 | extensions = [ 24 | 'sphinx.ext.autodoc', 25 | #'sphinx.ext.intersphinx', 26 | 'openstackdocstheme', 27 | ] 28 | 29 | # openstackdocstheme options 30 | openstackdocs_repo_name = 'openstack/sushy-tools' 31 | openstackdocs_pdf_link = True 32 | openstackdocs_use_storyboard = True 33 | 34 | # autodoc generation is a bit aggressive and a nuisance when doing heavy 35 | # text edit cycles. 36 | # execute "export SPHINX_DEBUG=1" in your terminal to disable 37 | 38 | # The suffix of source filenames. 39 | source_suffix = '.rst' 40 | 41 | # The master toctree document. 42 | master_doc = 'index' 43 | 44 | # General information about the project. 45 | copyright = '2016, OpenStack Foundation' 46 | 47 | # If true, '()' will be appended to :func: etc. cross-reference text. 48 | add_function_parentheses = True 49 | 50 | # If true, the current module name will be prepended to all description 51 | # unit titles (such as .. function::). 52 | add_module_names = True 53 | 54 | # The name of the Pygments (syntax highlighting) style to use. 55 | pygments_style = 'native' 56 | 57 | # -- Options for HTML output -------------------------------------------------- 58 | 59 | # The theme to use for HTML and HTML Help pages. Major themes that come with 60 | # Sphinx are currently 'default' and 'sphinxdoc'. 61 | # html_theme_path = ["."] 62 | # html_theme = '_theme' 63 | # html_static_path = ['static'] 64 | html_theme = 'openstackdocs' 65 | 66 | # Output file base name for HTML help builder. 67 | htmlhelp_basename = 'sushy-toolsdoc' 68 | 69 | # The openstackdocstheme 2.1.0 extension stopped always overriding latex_engine 70 | # to 'xelatex'. We need the 'xelatex' engine in order to handle some Unicode 71 | # characters we use in our feature classification matrix, like the "X" mark, so 72 | # we specify it here. 73 | latex_engine = 'xelatex' 74 | 75 | # Grouping the document tree into LaTeX files. List of tuples 76 | # (source start file, target name, title, author, documentclass 77 | # [howto/manual]). 78 | latex_documents = [ 79 | ('index', 80 | 'doc-sushy-tools.tex', 81 | 'Sushy Tools Documentation', 82 | 'OpenStack Foundation', 'manual'), 83 | ] 84 | 85 | # Example configuration for intersphinx: refer to the Python standard library. 86 | #intersphinx_mapping = {'http://docs.python.org/': None} 87 | -------------------------------------------------------------------------------- /doc/source/contributor/index.rst: -------------------------------------------------------------------------------- 1 | 2 | ============ 3 | Contributing 4 | ============ 5 | 6 | .. include:: ../../../CONTRIBUTING.rst 7 | 8 | 9 | Cloning the sushy-tools repository 10 | ++++++++++++++++++++++++++++++++++ 11 | 12 | If you haven't already, the sushy-tools source code should be pulled directly 13 | from git. 14 | 15 | .. code-block:: bash 16 | 17 | # from the directory where you want the source code to reside 18 | git clone https://opendev.org/openstack/sushy-tools 19 | 20 | 21 | Running the emulators locally 22 | +++++++++++++++++++++++++++++ 23 | 24 | Activate the virtual environment and run the emulator of your choice. For 25 | instance, to run the dynamic emulator: 26 | 27 | .. code-block:: bash 28 | 29 | tox -e venv -- sushy-emulator 30 | 31 | For more information on running the emulators, refer to the user docs for the 32 | :doc:`dynamic-emulator <../user/dynamic-emulator>` and the :doc:`static- 33 | emulator <../user/static-emulator>`. -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | 2 | .. include:: ../../README.rst 3 | 4 | Documentation 5 | ============= 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | 10 | install/index 11 | admin/index 12 | user/index 13 | contributor/index 14 | 15 | Indices and tables 16 | ================== 17 | 18 | * :ref:`genindex` 19 | * :ref:`modindex` 20 | * :ref:`search` 21 | -------------------------------------------------------------------------------- /doc/source/install/index.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | Installation 4 | ============ 5 | 6 | The sushy-tools Python package can be downloaded and installed with *pip*: 7 | 8 | .. code-block:: bash 9 | 10 | $ pip install sushy-tools 11 | 12 | Or, if you have virtualenvwrapper installed: 13 | 14 | .. code-block:: bash 15 | 16 | $ mkvirtualenv sushy-tools 17 | $ pip install sushy-tools 18 | 19 | The *Virtual Redfish BMC* tool relies upon one or more hypervisors to mimic bare 20 | metal nodes. Depending on the virtualization backend you are planning to use, 21 | certain third-party dependencies should also be installed. 22 | 23 | The dependencies for the virtualization backends that should be installed for 24 | the corresponding drivers to become operational are: 25 | 26 | * `libvirt-python` for the libvirt driver 27 | * `openstacksdk` for the nova driver 28 | 29 | .. note:: 30 | 31 | The dependencies for at least one virtualization backend should be satisfied 32 | to have the *Virtual Redfish BMC* emulator operational. 33 | -------------------------------------------------------------------------------- /doc/source/user/index.rst: -------------------------------------------------------------------------------- 1 | 2 | Using Redfish emulators 3 | ======================= 4 | 5 | The sushy-tools package includes two emulators - static and dynamic. 6 | 7 | The static emulator can be used to serve Redfish mocks in the form of static 8 | JSON documents. The dynamic emulator relies upon the `libvirt`, `OpenStack` or 9 | `Ironic` virtualization backends to mimic nodes behind a Redfish BMC. 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | static-emulator 15 | dynamic-emulator 16 | -------------------------------------------------------------------------------- /doc/source/user/static-emulator.rst: -------------------------------------------------------------------------------- 1 | 2 | Static Redfish BMC 3 | ================== 4 | 5 | The static Redfish responder is a simple REST API server which serves static 6 | contents down to the Redfish client. The tool emulates the simple read-only BMC. 7 | 8 | The user is expected to supply the Redfish-compliant contents, perhaps 9 | downloaded from the `DMTF `_ web site. For example, 10 | `this .zip archive `_ 11 | includes Redfish content mocks for Redfish 1.0.0. 12 | 13 | .. code-block:: bash 14 | 15 | curl -o DSP2043_1.0.0.zip \ 16 | https://www.dmtf.org/sites/default/files/standards/documents/DSP2043_1.0.0.zip 17 | unzip -d mockups DSP2043_1.0.0.zip 18 | sushy-static -m mockups/public-rackmount 19 | 20 | Once you have the static emulator running, you can use it as if it was a 21 | read-only bare metal controller listening at *localhost:8000* (by default): 22 | 23 | .. code-block:: bash 24 | 25 | curl http://localhost:8000/redfish/v1/Systems/ 26 | { 27 | "@odata.type": "#ComputerSystemCollection.ComputerSystemCollection", 28 | "Name": "Computer System Collection", 29 | "Members@odata.count": 1, 30 | "Members": [ 31 | { 32 | "@odata.id": "/redfish/v1/Systems/437XR1138R2" 33 | } 34 | ], 35 | "@odata.context": "/redfish/v1/$metadata#Systems", 36 | "@odata.id": "/redfish/v1/Systems", 37 | "@Redfish.Copyright": "Copyright 2014-2016 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 38 | } 39 | 40 | You can mock different Redfish versions as well as different bare metal 41 | machines by providing the appropriate Redfish contents. 42 | 43 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["pbr>=6.0.0", "setuptools>=64.0.0"] 3 | build-backend = "pbr.build" 4 | -------------------------------------------------------------------------------- /releasenotes/notes/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/releasenotes/notes/.placeholder -------------------------------------------------------------------------------- /releasenotes/notes/add-chassis-resource-aab57e319e6c7088.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds Chassis resource emulation to dynamic Redfish emulator. All emulated 5 | Computer Systems and Managers get linked up to the first of the configured 6 | Chassis (just one by default). 7 | -------------------------------------------------------------------------------- /releasenotes/notes/add-drive-resource-a827e136600071f3.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds Drive resource support to the dynamic Redfish emulator based on 5 | static user configuration. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/add-http-boot-uri-handling-c678bd89c7f6346c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds support to handle ``HttpBootUri`` being posted to the node, which 5 | maps to the virtual media functionality, because there is not a direct 6 | analog setting when interacting with libvirt. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/add-http-to-vmedia-ef04c156e66fc121.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds basic functionality for ``HttpBootUri`` to be passed through to 5 | the libvirt driver, enabling boot operations utilizing supplied media. 6 | This does not influence the default URL to boot from due to a lack of 7 | capability in libvirt, but instead treats it similar to virtual media. 8 | In this case, an override boot target of ``UefiHttp`` is also re-mapped 9 | to ``Cd`` to facilitate testing. 10 | -------------------------------------------------------------------------------- /releasenotes/notes/add-indicator-led-resource-672255319e6c3421.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds generic Indicator LED resource emulation based on static user 5 | configuration. Adds ``IndicatorLED`` property to Chassis and System 6 | resources. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/add-ironic-6f446bf16276b4dd.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | 5 | Adds new ironic driver to provide a limited emulated redfish API 6 | to ironic nodes. This would be needed in cases where a redfish 7 | compatible endpoint is needed but but don't have direct access 8 | to the BMC (you only have access via Ironic) or the BMC doesn't 9 | support redfish. 10 | -------------------------------------------------------------------------------- /releasenotes/notes/add-libvirt-firware-autoselect-b743737cb1cf9c5e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Support for domains utilizing firmware auto-selection has been added to 5 | the libvirt driver. 6 | 7 | -------------------------------------------------------------------------------- /releasenotes/notes/add-managers-resource-ffa58e329eccc058.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds Managers resource emulation to dynamic Redfish emulator. Emulated 5 | Computer Systems link up automatically to the first of the configured 6 | Managers (just one by default). 7 | -------------------------------------------------------------------------------- /releasenotes/notes/add-processor-schema-f23195a923f5830f.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds basic support for Redfish Processor schema. 5 | -------------------------------------------------------------------------------- /releasenotes/notes/add-simple-storage-resource-200e78d78c6aa8df.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds emulation support for Simple Storage resource to libvirt 5 | virtualization backend of the dynamic Redfish emulator. The emulation 6 | functionality assumes that the storage devices attached to a VM are 7 | configured as a libvirt Volume via a storage pool. Devices not configured 8 | as a Volume will not be considered for emulation purposes. 9 | -------------------------------------------------------------------------------- /releasenotes/notes/add-storage-resource-52bf30f50dd7b0a1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds Storage and Storage Controllers resource support to the dynamic 5 | Redfish emulator based on static user configuration. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/add-thermal-resource-8623543795643123.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds basic ``Thermal`` resource emulation under Chassis resource. The 5 | ``Thermal`` resource is not user-configurable, its contents is mostly 6 | static and depends on Jinja2 template. All references between resources 7 | are dynamically rendered though. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/add-updateservice-3332a9c15b5fb3ab.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds support for BIOS update emulation by introducing UpdateService. 5 | No actual updates are performed (all the code is doing is incrementing 6 | BIOS version value in libvirt xml) however this functionality may be 7 | used for automated testing of firmware update features in Ironic. Note 8 | BMC firmware update emulation is not supported at this time. 9 | -------------------------------------------------------------------------------- /releasenotes/notes/add-virtual-media-4a137a5fb5017031.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds Virtual Media resource. 5 | 6 | As of this release, user can configure a collection of virtual 7 | media devices including their types, names and other properties. 8 | By default, ``cdrom`` and ``floppy`` devices are configured. 9 | 10 | Each Manager automatically gets its own instance of the above 11 | mentioned virtual media device collection. HTTP/S-hosted images 12 | can be inserted into and ejected from virtual media devices. 13 | 14 | If libvirt virtualization backend is being used, once ISO image 15 | is inserted into the respective Manager, any System under that 16 | Manager can boot from that image by booting from its local ``cdrom`` 17 | over UEFI. 18 | 19 | The ISO images must be UEFI-bootable or hybrid. 20 | -------------------------------------------------------------------------------- /releasenotes/notes/add-volume-collection-to-storage-template-b673a7acfbb1a03e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Adds the ``Volumes`` navigation property to the ``Storage`` emulator 5 | template. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/add-volume-resource-db795af928e41e5c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds Volume resource emulation support. 5 | 6 | As of this release, a user can configure a collection of Volumes including 7 | the VolumeType and Capacity. The configured volumes will appear as libvirt 8 | volumes in the libvirt virtualization backend of the dynamic Redfish 9 | emulator (provided the libvirt pool specified for the volume exists). 10 | 11 | Volume creation via POST request is also supported. 12 | 13 | In case the Openstack backend is used, the NotSupportedError is raised. 14 | -------------------------------------------------------------------------------- /releasenotes/notes/add_message_and_attribute_registries-71e14a53bfa658da.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds support for returning message and attribute (particularly BIOS 5 | attribute) registries. Also adds two additional BIOS settings - 6 | 'SecureBootStatus' and 'SerialNumber'. 'SecureBootStatus' has 'ReadOnly' 7 | set to true and 'SecureBootStatus' has 'IsSystemUniqueProperty' in the 8 | registry. 9 | -------------------------------------------------------------------------------- /releasenotes/notes/auth-044dab149ab0c03f.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Supports HTTP basic authentication of Redfish endpoints. Set the new 5 | ``SUSHY_EMULATOR_AUTH_FILE`` variable to the path of an htpasswd file. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/auth-config-4f05bbfaea15bd2b.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes authentication when the configuration is provided via the 5 | ``--config`` option (as opposed to the environment). 6 | -------------------------------------------------------------------------------- /releasenotes/notes/auth-config-7705910e241909c1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes configuring the emulator via environment parameters when ``main`` 5 | is not invoked (e.g. when using WSGI). 6 | -------------------------------------------------------------------------------- /releasenotes/notes/bios-integer-types-79928fbd0b49ac57.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Allow non-string types to be configured for BIOS settings. 5 | -------------------------------------------------------------------------------- /releasenotes/notes/certificate-service-ff8061143d454313.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds basic support for custom TLS certificates with virtual media. 5 | -------------------------------------------------------------------------------- /releasenotes/notes/debug-mode-0dccdc3702c0590e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds an option --debug to enable debug mode when running sushy-emulator. 5 | 6 | -------------------------------------------------------------------------------- /releasenotes/notes/drop-python2-48e8388f96998244.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Python 2.7 support has been dropped. Last release of sushy-tools to 5 | support Python 2.7 is OpenStack Train. The minimum version of Python 6 | now supported by sushy-tools is Python 3.6. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/fakedriver-819d46b6f1e18081.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds a fake system driver (activated using the ``--fake`` argument) that 5 | does not have an actual backend and works by storing all values in 6 | the cache. 7 | 8 | It is currently functional enough for the Ironic's ``ramdisk`` deploy 9 | (and undeploy) to finish successfully. 10 | -------------------------------------------------------------------------------- /releasenotes/notes/feature-set-42f2846a17f59424.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Add a configuration options ``SUSHY_EMULATOR_FEATURE_SET`` to define which 5 | resources should be available. See the documentation for more details. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-bios-registry-handler-63528fb2e114a04c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes a bug in the handler for Bios/BiosRegistry - it should not include 5 | identity in order to match the url returned from 6 | bios_attribute_registry_file.json and be consistent with actual vendor 7 | responses. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-boot-mode-constant-71e6a7ec6b010273.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes one of the allowed values for ``BootSourceOverrideMode`` 5 | element - Redfish specification requires UEFI boot mode to be indicated 6 | as ``UEFI``, not ``Uefi`` as the dynamic Redfish emulator erroneously 7 | adopted. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-libvirt-boot-image-965d94fd30bca7b0.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes occasional failure when setting boot image to libvirt domain in 5 | response to virtual media "Insert" operation. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-libvirt-boot-mode-3ec2ccf49f96aec8.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes potential failure on reading/setting boot device of libvirt domain. 5 | Prior to this fix, boot device has only been respected in the boot loader 6 | part of libvirt domain XML. However per-device boot configuration can 7 | also be used. If the latter way is in place in a libvirt domain, reading 8 | boot device would yield nothing, while setting it in the boot loader would 9 | fail. This fix respects both ways of configuring libvirt boot device 10 | preferring the per-device configuration when setting new boot device. 11 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-libvirt-list-1ccf5a18bc89793d.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes libvirt domains listing so that both active and inactive domains 5 | are rendered as available Systems resource. Before this fix, only inactive 6 | domains were listed. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-libvirt-statefulness-0a2a7812d79fdd25.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Brings libvirt domain down prior to any change. When Redfish emulator 5 | is running against libvirt virtualization backend, any changes being 6 | done to domain are not applied for as long as the domain is up. This 7 | leads to two nuisances: 8 | 9 | + REST API is not really REST-ful meaning that successfully 10 | applied change is not reflected in the document tree 11 | + Multiple changes done to live domain XML tree may override one 12 | another because N-1 change done to a domain is not visible 13 | to N's change 14 | 15 | The fix is to bring running domain down briefly while the change is 16 | applied. 17 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-redirect-code-a0aa43251be7de0c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Changed HTTP redirect code from 302 to 307 to ensure the 5 | original HTTP method is left intact. Otherwise some clients 6 | may change PUT/POST to GET on redirect and effectively fail 7 | to perform the change they intended. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-retain-boot-device-5ae02731d2a000d3.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes situation when changing boot image could invalidate current boot 5 | device selection. The fix is to note current libvirt boot device and restore 6 | it if the image being changed resides on the active boot device. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-set-boot-mode-forbidden-085efb2aea6987ff.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes ``libvirtError: operation forbidden`` when setting boot mode 5 | 6 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-storage-drive-defaults-3a925a0063738b77.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes defaults to storage and drive resources. Prior to this fix, 5 | if storage and/or drive resource emulation is not set up via sushy 6 | emulator configuration, the clients trying to read either of 7 | these resources would hit hard HTTP 500 response. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-wsgi-config-e3f63f91152225f8.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes Flask config file load as pointed by the ``SUSHY_EMULATOR_CONFIG`` 5 | environment variable when running under WSGI server. Before this fix, 6 | the above mentioned environment variable was ignored and user config 7 | file not loaded. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/floppy-1be04ed78708caf1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes incorrect device name and bus when attaching a virtual floppy. 5 | -------------------------------------------------------------------------------- /releasenotes/notes/http-code-b4e32ad8485ef841.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Returns the correct status code (404) when a URL is not found. 5 | -------------------------------------------------------------------------------- /releasenotes/notes/no-boot-1709a77ecc140034.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | issues: 3 | - | 4 | Reads hardware state from libvirt domain XML 5 | 6 | Now reads boot device/mode/image from domain XML on 7 | filesystem rather then the running VM, thus avoiding 8 | the need for a reboot after setting something. The client 9 | should now power cycle the instance if the changes are 10 | required in the running VM. 11 | note: not simply a soft reboot. 12 | -------------------------------------------------------------------------------- /releasenotes/notes/novadriver-reduce-api-calls-934eb2af95ea2419.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | The nova emulator driver makes fewer API calls when retrieving the 5 | server list. This significantly improves the API response times when 6 | using the emulator in a project with many instances and ports per 7 | instance. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/preserve-libvirt-domain-info-955410f570060241.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | security: 3 | - | 4 | Secure information in the Libvirt domain XML document is now preserved. 5 | For more information, please see `story 2010382 6 | `_. 7 | fixes: 8 | - | 9 | Fixes an issue where secure fields were accidentally lost in the Libvirt 10 | domain XML document. 11 | -------------------------------------------------------------------------------- /releasenotes/notes/py36-37-bye-bye-5a0cfafa9cfe8230.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Sushy-tools does not support Python 3.6 and 3.7 anymore, please use 5 | version 3.8 or higher. 6 | 7 | -------------------------------------------------------------------------------- /releasenotes/notes/q35-aea1a63ff2c7c72b.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | No longer tries to use IDE controllers on q35 machines. 5 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-py38-ec073d8462e60e8b.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Support for Python 3.8 has been removed. Now the minimum python version 5 | supported is 3.9 . 6 | -------------------------------------------------------------------------------- /releasenotes/notes/secure-boot-de663109ced9b266.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | It is now possible to enable and disable UEFI Secure Boot mode via Redfish 5 | requests for the libvirt driver. This is possible by configuring domains to 6 | use a secure boot capable firmware loader, and setting configuration values 7 | `SUSHY_EMULATOR_SECURE_BOOT_ENABLED_NVRAM` and 8 | `SUSHY_EMULATOR_SECURE_BOOT_DISABLED_NVRAM` to nvram template paths which 9 | enable or disable secure boot. 10 | 11 | The fake driver supports getting and setting secure boot, the nova driver 12 | only supports getting. -------------------------------------------------------------------------------- /releasenotes/notes/unsupported-http-status-code-05af22dbecc517f7.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | The HTTP status code for NotSupportedError has been fixed. The generic 5 | ``500 Internal Server Error`` status code was previously used, it will now 6 | use the ``501 Not Implemented`` status code. 7 | 8 | -------------------------------------------------------------------------------- /releasenotes/notes/updatable-firmware-versions-b1f947339b2e6b94.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Replaces hardcoded BIOS version string with updatable field backed by a 5 | custom section in libvirt metadata (similar to prior art on BIOS 6 | settings). This can be used for BIOS version information as well as 7 | storing firmware version information of other components. NOTE: this 8 | enhancement is meant to facilitate testing BIOS/firmware upgrade codepaths 9 | and NOT performing actual BIOS/firmware upgrades. 10 | -------------------------------------------------------------------------------- /releasenotes/notes/updateservice-redirect-workaround-97c4864ef20a2eef.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Resolves the issue where UpdateService.SimpleUpdate action cannot 5 | complete successfully due to connections from UpdateService to System 6 | being redirected by libvirt system driver alias handling code. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/verify-certificate-798f84905cee03e5.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Supports reading and changing ``VerifyCertificate`` in ``VirtualMedia`` 5 | resources. 6 | upgrade: 7 | - | 8 | The default value of ``SUSHY_EMULATOR_VMEDIA_VERIFY_SSL`` has been changed 9 | to ``False`` to match the actual bare metal hardware. 10 | -------------------------------------------------------------------------------- /releasenotes/notes/virtual-media-alternate-bus-6d984ae45acb7ac4.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds basic support for virtual media devices on non-IDE buses, such as SATA and 5 | SCSI, as IDE devices are not supported on Q35 libvirt domains. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/vmedia-credentials-0f6e1f539bd94d14.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | The emulator now supports providing ``UserName`` and ``Password`` for 5 | virtual media. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/vmedia-error-73fda32cf6046554.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes handling HTTP errors on downloading virtual media files. Error codes 5 | are no longer silently ignored. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/vmedia-openstack-fc422b845c343fc3.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | The openstack driver now supports insert and eject of virtual media. On 5 | insert a new empty volume is created and attached to the server and the 6 | server is rebuilt with the that image. On eject it is assumed that the 7 | attached volume has been rewritten with bootable image data. The volume is 8 | detached and uploaded as an image, then the server is rebuilt with that 9 | image. 10 | 11 | Both insert and delete results in the root disk being wiped and replaced 12 | with the contents of an image, so this should not be used in any scenario 13 | where the root disk data needs to be retained. -------------------------------------------------------------------------------- /releasenotes/notes/vmedia-openstack-file-upload-8a6843d6d0b33772.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | The openstack driver for virtual media now supports Glance file upload. 5 | As an alternative to using image import via the ``web-download`` method, 6 | it is now possible to configure the emulator to download the image to a 7 | temp file which is then uploaded to Glance. 8 | 9 | Set the ``SUSHY_EMULATOR_OS_VMEDIA_IMAGE_FILE_UPLOAD`` option to ``True`` 10 | to enable Glance file upload. 11 | 12 | -------------------------------------------------------------------------------- /releasenotes/notes/vmedia-system-c254f4d0918d8b91.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Exposes the VirtualMedia resource directly on Systems, not just Managers. 5 | -------------------------------------------------------------------------------- /releasenotes/source/_static/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/releasenotes/source/_static/.placeholder -------------------------------------------------------------------------------- /releasenotes/source/_templates/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/releasenotes/source/_templates/.placeholder -------------------------------------------------------------------------------- /releasenotes/source/index.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | sushy-tools Release Notes 3 | ========================= 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | unreleased 9 | -------------------------------------------------------------------------------- /releasenotes/source/unreleased.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Current Series Release Notes 3 | ============================== 4 | 5 | .. release-notes:: 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Requirements lower bounds listed here are our best effort to keep them up to 2 | # date but we do not test them so no guarantee of having them all correct. If 3 | # you find any incorrect lower bounds, let us know or propose a fix. 4 | 5 | # The order of packages is significant, because pip processes them in the order 6 | # of appearance. Changing the order has an impact on the overall integration 7 | # process, which may cause wedges in the gate later. 8 | 9 | pbr>=6.0.0 # Apache-2.0 10 | Flask>=1.0.2 # BSD 11 | requests>=2.14.2 # Apache-2.0 12 | tenacity>=6.2.0 # Apache-2.0 13 | bcrypt>=3.1.3 # Apache-2.0 14 | WebOb>=1.7.1 # MIT 15 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = sushy-tools 3 | summary = A set of tools to support the development and test of the Sushy library (https://docs.openstack.org/sushy/) 4 | description_file = 5 | README.rst 6 | license = Apache-2.0 7 | author = OpenStack 8 | author_email = openstack-discuss@lists.openstack.org 9 | home_page = https://docs.openstack.org/sushy/latest/ 10 | python_requires = >=3.9 11 | classifier = 12 | Environment :: OpenStack 13 | Intended Audience :: Information Technology 14 | Intended Audience :: System Administrators 15 | Operating System :: POSIX :: Linux 16 | Programming Language :: Python 17 | Programming Language :: Python :: Implementation :: CPython 18 | Programming Language :: Python :: 3 :: Only 19 | Programming Language :: Python :: 3 20 | Programming Language :: Python :: 3.9 21 | Programming Language :: Python :: 3.10 22 | Programming Language :: Python :: 3.11 23 | Programming Language :: Python :: 3.12 24 | 25 | [files] 26 | packages = 27 | sushy_tools 28 | 29 | [entry_points] 30 | console_scripts = 31 | sushy-emulator = sushy_tools.emulator.main:main 32 | sushy-static = sushy_tools.static.main:main 33 | 34 | [codespell] 35 | quiet-level = 4 36 | # Words to ignore: 37 | # hda: Hardware Device Address 38 | # assertIn: Python's unittest method 39 | ignore-words-list = hda,assertIn 40 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import setuptools 17 | 18 | setuptools.setup( 19 | setup_requires=['pbr>=6.0.0'], 20 | pbr=True) 21 | -------------------------------------------------------------------------------- /sushy_tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/__init__.py -------------------------------------------------------------------------------- /sushy_tools/emulator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/emulator/__init__.py -------------------------------------------------------------------------------- /sushy_tools/emulator/api_utils.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import functools 14 | 15 | import flask 16 | 17 | 18 | def debug(*args, **kwargs): 19 | flask.current_app.logger.debug(*args, **kwargs) 20 | 21 | 22 | def info(*args, **kwargs): 23 | flask.current_app.logger.info(*args, **kwargs) 24 | 25 | 26 | def warning(*args, **kwargs): 27 | flask.current_app.logger.warning(*args, **kwargs) 28 | 29 | 30 | def error(*args, **kwargs): 31 | flask.current_app.logger.error(*args, **kwargs) 32 | 33 | 34 | def instance_denied(**kwargs): 35 | deny = True 36 | 37 | try: 38 | deny = (kwargs['identity'] not in 39 | flask.current_app.config['SUSHY_EMULATOR_ALLOWED_INSTANCES']) 40 | 41 | except KeyError: 42 | deny = False 43 | 44 | finally: 45 | if deny: 46 | warning('Instance %s access denied', kwargs.get('identity')) 47 | 48 | return deny 49 | 50 | 51 | def ensure_instance_access(decorated_func): 52 | @functools.wraps(decorated_func) 53 | def decorator(*args, **kwargs): 54 | if instance_denied(**kwargs): 55 | raise error.NotFound() 56 | 57 | return decorated_func(*args, **kwargs) 58 | 59 | return decorator 60 | 61 | 62 | def returns_json(decorated_func): 63 | @functools.wraps(decorated_func) 64 | def decorator(*args, **kwargs): 65 | response = decorated_func(*args, **kwargs) 66 | if isinstance(response, flask.Response): 67 | return response 68 | if isinstance(response, tuple): 69 | contents, status, *headers = response 70 | else: 71 | contents, status, headers = response, 200, () 72 | kwargs = {'headers': headers[0]} if headers else {} 73 | return flask.Response(response=contents, status=status, 74 | content_type='application/json', **kwargs) 75 | 76 | return decorator 77 | -------------------------------------------------------------------------------- /sushy_tools/emulator/auth_basic.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import base64 17 | import binascii 18 | import logging 19 | 20 | import bcrypt 21 | import webob 22 | 23 | from sushy_tools import error 24 | 25 | LOG = logging.getLogger(__name__) 26 | 27 | 28 | class BasicAuthMiddleware(object): 29 | """Middleware which performs HTTP basic authentication on requests 30 | 31 | """ 32 | def __init__(self, app, auth_file): 33 | self.app = app 34 | self.auth_file = auth_file 35 | validate_auth_file(auth_file) 36 | 37 | def format_exception(self, e): 38 | result = {'error': {'message': str(e), 'code': e.code}} 39 | headers = list(e.headers.items()) + [ 40 | ('Content-Type', 'application/json') 41 | ] 42 | return webob.Response(content_type='application/json', 43 | status_code=e.code, 44 | json_body=result, 45 | headerlist=headers) 46 | 47 | def __call__(self, env, start_response): 48 | 49 | try: 50 | token = parse_header(env) 51 | username, password = parse_token(token) 52 | env.update(authenticate(self.auth_file, username, password)) 53 | 54 | return self.app(env, start_response) 55 | 56 | except error.FishyError as e: 57 | response = self.format_exception(e) 58 | return response(env, start_response) 59 | 60 | 61 | def authenticate(auth_file, username, password): 62 | """Finds username and password match in Apache style user auth file 63 | 64 | The user auth file format is expected to comply with Apache 65 | documentation[1] however the bcrypt password digest is the *only* 66 | digest format supported. 67 | 68 | [1] https://httpd.apache.org/docs/current/misc/password_encryptions.html 69 | 70 | :param: auth_file: Path to user auth file 71 | :param: username: Username to authenticate 72 | :param: password: Password encoded as bytes 73 | :returns: A dictionary of WSGI environment values to append to the request 74 | :raises: Unauthorized, if no file entries match supplied username/password 75 | """ 76 | line_prefix = username + ':' 77 | try: 78 | with open(auth_file, 'r') as f: 79 | for line in f: 80 | entry = line.strip() 81 | if entry and entry.startswith(line_prefix): 82 | return auth_entry(entry, password) 83 | except OSError as exc: 84 | LOG.error('Problem reading auth user file: %s', exc) 85 | raise error.ConfigInvalid(msg='Problem reading auth user file') 86 | 87 | # reached end of file with no matches 88 | LOG.info('User %s not found', username) 89 | unauthorized() 90 | 91 | 92 | def auth_entry(entry, password): 93 | """Compare a password with a single user auth file entry 94 | 95 | :param: entry: Line from auth user file to use for authentication 96 | :param: password: Password encoded as bytes 97 | :returns: A dictionary of WSGI environment values to append to the request 98 | :raises: Unauthorized, if the entry doesn't match supplied password or 99 | if the entry is encrypted with a method other than bcrypt 100 | """ 101 | username, encrypted = parse_entry(entry) 102 | 103 | if not bcrypt.checkpw(password, encrypted): 104 | LOG.info('Password for %s does not match', username) 105 | unauthorized() 106 | 107 | return { 108 | 'HTTP_X_USER': username, 109 | 'HTTP_X_USER_NAME': username 110 | } 111 | 112 | 113 | def validate_auth_file(auth_file): 114 | """Read the auth user file and validate its correctness 115 | 116 | :param: auth_file: Path to user auth file 117 | :raises: ConfigInvalid on validation error 118 | """ 119 | try: 120 | with open(auth_file, 'r') as f: 121 | for line in f: 122 | entry = line.strip() 123 | if entry and ':' in entry: 124 | parse_entry(entry) 125 | except OSError: 126 | raise error.ConfigInvalid( 127 | msg='Problem reading auth user file: %s' % auth_file) 128 | 129 | 130 | def parse_entry(entry): 131 | """Extrace the username and encrypted password from a user auth file entry 132 | 133 | :param: entry: Line from auth user file to use for authentication 134 | :returns: a tuple of username and encrypted password 135 | :raises: ConfigInvalid if the password is not in the supported bcrypt 136 | format 137 | """ 138 | username, encrypted_str = entry.split(':', maxsplit=1) 139 | encrypted = encrypted_str.encode('utf-8') 140 | 141 | if encrypted[:4] not in (b'$2y$', b'$2a$', b'$2b$'): 142 | error_msg = ('Only bcrypt digested passwords are supported for ' 143 | '%(username)s') % {'username': username} 144 | raise error.ConfigInvalid(msg=error_msg) 145 | return username, encrypted 146 | 147 | 148 | def parse_token(token): 149 | """Parse the token portion of the Authentication header value 150 | 151 | :param: token: Token value from basic authorization header 152 | :returns: tuple of username, password 153 | :raises: Unauthorized, if username and password could not be parsed for any 154 | reason 155 | """ 156 | try: 157 | if isinstance(token, str): 158 | token = token.encode('utf-8') 159 | auth_pair = base64.b64decode(token, validate=True) 160 | (username, password) = auth_pair.split(b':', maxsplit=1) 161 | 162 | return (username.decode('utf-8'), password) 163 | except (TypeError, binascii.Error, ValueError) as exc: 164 | LOG.info('Could not decode authorization token: %s', exc) 165 | raise error.BadRequest('Could not decode authorization token') 166 | 167 | 168 | def parse_header(env): 169 | """Parse WSGI environment for Authorization header of type Basic 170 | 171 | :param: env: WSGI environment to get header from 172 | :returns: Token portion of the header value 173 | :raises: Unauthorized, if header is missing or if the type is not Basic 174 | """ 175 | try: 176 | auth_header = env.pop('HTTP_AUTHORIZATION') 177 | except KeyError: 178 | LOG.info('No authorization token received') 179 | unauthorized('Authorization required') 180 | try: 181 | auth_type, token = auth_header.strip().split(maxsplit=1) 182 | except (ValueError, AttributeError) as exc: 183 | LOG.info('Could not parse Authorization header: %s', exc) 184 | raise error.BadRequest('Could not parse Authorization header') 185 | 186 | if auth_type.lower() != 'basic': 187 | msg = ('Unsupported authorization type "%s"') % auth_type 188 | LOG.info(msg) 189 | raise error.BadRequest(msg) 190 | return token 191 | 192 | 193 | def unauthorized(message=None): 194 | """Raise an Unauthorized exception to prompt for basic authentication 195 | 196 | :param: message: Optional message for esception 197 | :raises: Unauthorized with WWW-Authenticate header set 198 | """ 199 | if not message: 200 | message = 'Incorrect username or password' 201 | raise error.Unauthorized(message) 202 | -------------------------------------------------------------------------------- /sushy_tools/emulator/base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | 17 | class DriverBase(object): 18 | """Common base for Redfish Systems, Managers and Chassis models""" 19 | 20 | def __init__(self, config, logger): 21 | self._config = config 22 | self._logger = logger 23 | -------------------------------------------------------------------------------- /sushy_tools/emulator/constants.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | # 16 | 17 | DEVICE_TYPE_PXE = 'Pxe' 18 | DEVICE_TYPE_HDD = 'Hdd' 19 | DEVICE_TYPE_CD = 'Cd' 20 | DEVICE_TYPE_FLOPPY = 'Floppy' 21 | -------------------------------------------------------------------------------- /sushy_tools/emulator/controllers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/emulator/controllers/__init__.py -------------------------------------------------------------------------------- /sushy_tools/emulator/controllers/certificate_service.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import re 14 | 15 | import flask 16 | 17 | from sushy_tools.emulator import api_utils 18 | from sushy_tools import error 19 | 20 | 21 | certificate_service = flask.Blueprint( 22 | 'CertificateService', __name__, 23 | url_prefix='/redfish/v1/CertificateService') 24 | 25 | 26 | _VMEDIA_URI_PATTERN = re.compile( 27 | '/redfish/v1/Managers/([^/]+)/VirtualMedia/([^/]+)/Certificates/([^/]+)/?$' 28 | ) 29 | 30 | 31 | @certificate_service.route('', methods=['GET']) 32 | @api_utils.returns_json 33 | def certificate_service_resource(): 34 | api_utils.debug('Serving certificate service') 35 | return flask.render_template('certificate_service.json') 36 | 37 | 38 | @certificate_service.route('/CertificateLocations', methods=['GET']) 39 | @api_utils.returns_json 40 | def certificate_service_locations(): 41 | api_utils.debug('Serving certificate locations') 42 | locations = [] 43 | for mgr in flask.current_app.managers.managers: 44 | for dev in flask.current_app.vmedia.devices: 45 | try: 46 | certs = flask.current_app.vmedia.list_certificates(mgr, dev) 47 | except error.NotFound: 48 | api_utils.debug('No certificates for manager %s, virtual ' 49 | 'media device %s', mgr, dev) 50 | continue 51 | 52 | for cert in certs: 53 | locations.append( 54 | f'/redfish/v1/Managers/{mgr}/VirtualMedia/{dev}' 55 | f'/Certificates/{cert.id}' 56 | ) 57 | 58 | return flask.render_template('certificate_locations.json', 59 | certificates=locations) 60 | 61 | 62 | @certificate_service.route('/Actions/CertificateService.ReplaceCertificate', 63 | methods=['POST']) 64 | @api_utils.ensure_instance_access 65 | @api_utils.returns_json 66 | def certificate_service_replace_certificate(): 67 | if not flask.request.json: 68 | raise error.BadRequest("Empty or malformed certificate") 69 | 70 | try: 71 | cert_string = flask.request.json['CertificateString'] 72 | cert_type = flask.request.json['CertificateType'] 73 | cert_uri = flask.request.json['CertificateUri'] 74 | except KeyError as exc: 75 | raise error.BadRequest(f"Missing required parameter {exc}") 76 | 77 | match = _VMEDIA_URI_PATTERN.search(cert_uri) 78 | if not match: 79 | raise error.NotFound( 80 | f"Certificates at URI {cert_uri} are not supported") 81 | 82 | if cert_type != 'PEM': 83 | raise error.BadRequest( 84 | f"Only PEM certificates are supported, got {cert_type}") 85 | 86 | manager_id, device, cert_id = match.groups() 87 | 88 | flask.current_app.managers.get_manager(manager_id) 89 | flask.current_app.vmedia.replace_certificate( 90 | manager_id, device, cert_id, cert_string, cert_type) 91 | 92 | return '', 204 93 | -------------------------------------------------------------------------------- /sushy_tools/emulator/controllers/update_service.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import flask 14 | 15 | from sushy_tools.emulator import api_utils 16 | from sushy_tools import error 17 | 18 | 19 | update_service = flask.Blueprint( 20 | 'UpdateService', __name__, 21 | url_prefix='/redfish/v1/UpdateService/') 22 | 23 | 24 | @update_service.route('', methods=['GET']) 25 | @api_utils.returns_json 26 | def update_service_resource(): 27 | api_utils.debug('Serving update service resources') 28 | 29 | return flask.render_template( 30 | 'update_service.json' 31 | ) 32 | 33 | 34 | @update_service.route('/Actions/UpdateService.SimpleUpdate', 35 | methods=['POST']) 36 | @api_utils.returns_json 37 | def update_service_simple_update(): 38 | image_uri = flask.request.json.get('ImageURI') 39 | targets = flask.request.json.get('Targets') 40 | api_utils.debug('Received Targets: "%s"', targets) 41 | if not image_uri or not targets: 42 | message = "Missing ImageURI and/or Targets." 43 | return flask.render_template('error.json', message=message), 400 44 | 45 | for target in targets: 46 | # NOTE(janders) since we only support BIOS let's ignore Manager targets 47 | if "Manager" in target: 48 | message = "Manager is not currently a supported Target." 49 | return flask.render_template('error.json', message=message), 400 50 | try: 51 | name = target.rstrip("/").rsplit('/', 1)[-1] 52 | uuid = flask.current_app.systems.uuid(name) 53 | except error.AliasAccessError as exc: 54 | api_utils.debug('Received a redirect in response to GET System ' 55 | '"%s". New System ID: "%s"', name, exc) 56 | uuid = str(exc) 57 | except Exception as exc: 58 | api_utils.debug('Encountered exception "%s" while attempting to ' 59 | 'GET System "%s"', exc, name) 60 | raise 61 | 62 | # NOTE(janders) iterate over the array? narrow down which one is needed 63 | # first? I suppose the former since we may want to update multiple 64 | api_utils.debug('Fetching BIOS information for System "%s"', 65 | uuid) 66 | try: 67 | versions = flask.current_app.systems.get_versions(uuid) 68 | 69 | except error.NotSupportedError as ex: 70 | api_utils.warning( 71 | 'System failed to fetch BIOS information with exception %s', 72 | ex) 73 | message = "Failed fetching BIOS information" 74 | return flask.render_template('error.json', message=message), 500 75 | 76 | bios_version = versions.get('BiosVersion') 77 | 78 | api_utils.debug('Current BIOS version for System "%s" is "%s" ,' 79 | 'attempting upgrade.', 80 | uuid, bios_version) 81 | 82 | bios_version = bios_version.split('.') 83 | bios_version[1] = str(int(bios_version[1]) + 1) 84 | bios_version = '.'.join(bios_version) 85 | firmware_versions = {"BiosVersion": bios_version} 86 | 87 | try: 88 | flask.current_app.systems.set_versions(uuid, firmware_versions) 89 | except error.NotSupportedError as ex: 90 | api_utils.warning('System failed to update bios with exception %s', 91 | ex) 92 | message = "Failed updating BIOS version" 93 | return flask.render_template('error.json', message=message), 500 94 | 95 | api_utils.info( 96 | 'Emulated BIOS upgrade has been successful for ' 97 | 'System %s, new version is "%s".', uuid, bios_version) 98 | return '', 204, {'Location': '/redfish/v1/TaskService/Tasks/42'} 99 | -------------------------------------------------------------------------------- /sushy_tools/emulator/memoize.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | try: 17 | from collections import abc as collections 18 | 19 | except ImportError: 20 | import collections 21 | 22 | import contextlib 23 | from functools import wraps 24 | import os 25 | import pickle 26 | import sqlite3 27 | import tempfile 28 | 29 | import tenacity 30 | 31 | # Python 3.8 32 | MutableMapping = getattr(collections, 'abc', collections).MutableMapping 33 | 34 | 35 | def memoize(permanent_cache=None): 36 | """Cache the return value of the decorated method. 37 | 38 | :param permanent_cache: a `dict` like object to use as a cache. 39 | If not given, the `._cache` attribute would be added to 40 | the object of the decorated method pointing to a newly 41 | created `dict`. 42 | :return: decorated function 43 | """ 44 | 45 | def decorator(method): 46 | 47 | @wraps(method) 48 | def wrapped(self, *args, **kwargs): 49 | if permanent_cache is None: 50 | try: 51 | cache = self._cache 52 | 53 | except AttributeError: 54 | cache = self._cache = {} 55 | 56 | else: 57 | cache = permanent_cache 58 | 59 | method_cache = cache.setdefault(method, {}) 60 | 61 | key = frozenset(args), frozenset(kwargs) 62 | 63 | try: 64 | return method_cache[key] 65 | 66 | except KeyError: 67 | rv = method(self, *args, **kwargs) 68 | method_cache[key] = rv 69 | return rv 70 | 71 | return wrapped 72 | 73 | return decorator 74 | 75 | 76 | _retry = tenacity.retry( 77 | retry=tenacity.retry_if_exception_type(sqlite3.OperationalError), 78 | wait=tenacity.wait_exponential(min=0.1, max=2, multiplier=1), 79 | stop=tenacity.stop_after_delay(5), 80 | reraise=True) 81 | 82 | 83 | class PersistentDict(MutableMapping): 84 | DBPATH = os.path.join(tempfile.gettempdir(), 'sushy-emulator') 85 | 86 | _dbpath = None 87 | 88 | def make_permanent(self, dbpath, dbfile): 89 | dbpath = dbpath or self.DBPATH 90 | os.makedirs(dbpath, exist_ok=True) 91 | 92 | self._dbpath = os.path.join(dbpath, dbfile) + '.sqlite' 93 | 94 | with self.connection() as cursor: 95 | cursor.execute( 96 | 'create table if not exists cache ' 97 | '(key blob primary key not null, value blob not null)' 98 | ) 99 | 100 | @staticmethod 101 | def encode(obj): 102 | return pickle.dumps(obj) 103 | 104 | @staticmethod 105 | def decode(blob): 106 | return pickle.loads(blob) 107 | 108 | @contextlib.contextmanager 109 | def connection(self): 110 | if not self._dbpath: 111 | raise TypeError('Dict is not yet persistent') 112 | 113 | with sqlite3.connect(self._dbpath) as connection: 114 | connection.execute("pragma journal_mode=wal") 115 | yield connection.cursor() 116 | 117 | @_retry 118 | def __getitem__(self, key): 119 | key = self.encode(key) 120 | 121 | with self.connection() as cursor: 122 | cursor.execute( 123 | 'select value from cache where key=?', 124 | (key,) 125 | ) 126 | value = cursor.fetchone() 127 | 128 | if value is None: 129 | raise KeyError(key) 130 | 131 | return self.decode(value[0]) 132 | 133 | @_retry 134 | def __setitem__(self, key, value): 135 | key = self.encode(key) 136 | value = self.encode(value) 137 | 138 | with self.connection() as cursor: 139 | cursor.execute( 140 | 'insert or replace into cache values (?, ?)', 141 | (key, value) 142 | ) 143 | 144 | @_retry 145 | def __delitem__(self, key): 146 | key = self.encode(key) 147 | 148 | with self.connection() as cursor: 149 | cursor.execute( 150 | 'delete from cache where key=?', 151 | (key,) 152 | ) 153 | if not cursor.rowcount: 154 | raise KeyError(key) 155 | 156 | @_retry 157 | def __iter__(self): 158 | with self.connection() as cursor: 159 | cursor.execute( 160 | 'select key from cache' 161 | ) 162 | records = cursor.fetchall() 163 | 164 | for r in records: 165 | yield self.decode(r[0]) 166 | 167 | @_retry 168 | def __len__(self): 169 | with self.connection() as cursor: 170 | cursor.execute( 171 | 'select count(*) from cache' 172 | ) 173 | count = cursor.fetchone()[0] 174 | return count 175 | -------------------------------------------------------------------------------- /sushy_tools/emulator/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/emulator/resources/__init__.py -------------------------------------------------------------------------------- /sushy_tools/emulator/resources/base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | 17 | class DriverBase(object): 18 | """Common base for emulated Redfish resource drivers""" 19 | 20 | def __init__(self, config, logger): 21 | """Initialize a driver. 22 | 23 | :params config: system configuration dict 24 | :params logger: system logger object 25 | """ 26 | self._config = config 27 | self._logger = logger 28 | -------------------------------------------------------------------------------- /sushy_tools/emulator/resources/chassis.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import uuid 17 | 18 | from sushy_tools.emulator import base 19 | from sushy_tools import error 20 | 21 | 22 | class StaticDriver(base.DriverBase): 23 | """Redfish chassis backed by configuration file""" 24 | 25 | def __init__(self, config, logger): 26 | super().__init__(config, logger) 27 | 28 | chassis = self._config.get('SUSHY_EMULATOR_CHASSIS') 29 | if not chassis: 30 | # Default chassis 31 | chassis = [ 32 | { 33 | u'Id': u'SheetMetalChassis', 34 | u'Name': u'Chassis', 35 | u'UUID': u'15693887-7984-9484-3272-842188918912', 36 | } 37 | ] 38 | 39 | self._chassis_by_id = { 40 | x['Id']: x for x in chassis 41 | } 42 | self._chassis_by_uuid = { 43 | x['UUID']: x for x in chassis if 'UUID' in x 44 | } 45 | self._chassis_by_name = { 46 | x['Name']: x for x in chassis if 'Name' in x 47 | } 48 | 49 | if len(self._chassis_by_uuid) != len(chassis): 50 | raise error.FishyError( 51 | 'Conflicting UUIDs in static chassis configuration') 52 | 53 | def _get_chassis(self, identity): 54 | try: 55 | uu_identity = str(uuid.UUID(identity)) 56 | 57 | return self._chassis_by_uuid[uu_identity] 58 | 59 | except (ValueError, KeyError): 60 | try: 61 | uu_identity = self._chassis_by_name[identity]['UUID'] 62 | 63 | except KeyError: 64 | 65 | try: 66 | uu_identity = self._chassis_by_id[identity]['UUID'] 67 | 68 | except KeyError: 69 | msg = ('Error finding chassis by UUID/Name/Id ' 70 | '"%(identity)s"' % {'identity': identity}) 71 | 72 | self._logger.debug(msg) 73 | 74 | raise error.FishyError(msg) 75 | 76 | raise error.AliasAccessError(uu_identity) 77 | 78 | @property 79 | def driver(self): 80 | """Return human-friendly driver information 81 | 82 | :returns: driver information as `str` 83 | """ 84 | return '' 85 | 86 | @property 87 | def chassis(self): 88 | """Return available Redfish chassis 89 | 90 | :returns: list of UUIDs representing the chassis 91 | """ 92 | return sorted(self._chassis_by_uuid) 93 | 94 | def uuid(self, identity): 95 | """Get Redfish chassis UUID 96 | 97 | The universal unique identifier (UUID) for this system. Can be used 98 | in place of chassis name if there are duplicates. 99 | 100 | If driver backend does not support non-unique chassis identity, 101 | this method may just return the `identity`. 102 | 103 | :returns: Redfish chassis UUID 104 | """ 105 | chassis = self._get_chassis(identity) 106 | return chassis.get('UUID') 107 | 108 | def name(self, identity): 109 | """Get Redfish chassis name by UUID 110 | 111 | The universal unique identifier (UUID) for this Redfish chassis. 112 | Can be used in place of chassis name if there are duplicates. 113 | 114 | If driver backend does not support chassis names, this method may 115 | just return the `identity`. 116 | 117 | :returns: Redfish chassis name 118 | """ 119 | chassis = self._get_chassis(identity) 120 | return chassis.get('Name') 121 | -------------------------------------------------------------------------------- /sushy_tools/emulator/resources/drives.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import uuid 17 | 18 | from sushy_tools.emulator.resources import base 19 | from sushy_tools import error 20 | 21 | 22 | class StaticDriver(base.DriverBase): 23 | """Redfish storage drives backed by configuration file""" 24 | 25 | def __init__(self, config, logger): 26 | super().__init__(config, logger) 27 | self._drives = self._config.get('SUSHY_EMULATOR_DRIVES', {}) 28 | 29 | @property 30 | def driver(self): 31 | """Return human-friendly driver information 32 | 33 | :returns: driver information as `str` 34 | """ 35 | return '' 36 | 37 | def get_drives(self, identity, storage_id): 38 | try: 39 | uu_identity = str(uuid.UUID(identity)) 40 | 41 | return self._drives[(uu_identity, storage_id)] 42 | 43 | except (ValueError, KeyError): 44 | msg = ('Error finding drive for System UUID "%s" and Storage ID ' 45 | '"%s"', identity, storage_id) 46 | 47 | self._logger.debug(msg) 48 | 49 | raise error.FishyError(msg) 50 | 51 | def get_all_drives(self): 52 | """Return all drives represented as tuples in the following format: 53 | 54 | (System_UUID, Storage_ID, Drive_ID) 55 | 56 | :returns: list of tuples representing the drives 57 | """ 58 | return [k + (d['Id'],) for k in self._drives 59 | for d in self._drives[k]] 60 | -------------------------------------------------------------------------------- /sushy_tools/emulator/resources/indicators.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from sushy_tools.emulator import memoize 17 | from sushy_tools.emulator.resources import base 18 | from sushy_tools import error 19 | 20 | 21 | class StaticDriver(base.DriverBase): 22 | """Redfish indicator LED simulator 23 | 24 | Maintain indicators states in memory. Does not light up 25 | anything. 26 | """ 27 | 28 | def __init__(self, config, logger): 29 | super().__init__(config, logger) 30 | self._indicators = memoize.PersistentDict() 31 | if hasattr(self._indicators, 'make_permanent'): 32 | self._indicators.make_permanent( 33 | self._config.get('SUSHY_EMULATOR_STATE_DIR'), 'indicators') 34 | self._indicators.update( 35 | self._config.get('SUSHY_EMULATOR_INDICATOR_LEDS', {})) 36 | 37 | @property 38 | def driver(self): 39 | """Return human-friendly driver information 40 | 41 | :returns: driver information as `str` 42 | """ 43 | return '' 44 | 45 | @property 46 | def indicators(self): 47 | """Return available Redfish indicators 48 | 49 | :returns: list of UUIDs representing the indicators 50 | """ 51 | return list(self._indicators) 52 | 53 | def get_indicator_state(self, identity): 54 | """Get indicator state 55 | 56 | :param identity: indicator identity 57 | 58 | :returns: indicator state as one of *Lit*, *Blinking*, 59 | *Off* 60 | """ 61 | if identity not in self._indicators: 62 | self._indicators[identity] = 'Lit' 63 | 64 | return self._indicators[identity] 65 | 66 | def set_indicator_state(self, identity, state): 67 | """Set indicator state 68 | 69 | :param identity: indicator identity 70 | :param state: indicator state as one of *Lit*, *Blinking*, *Off* 71 | 72 | :raises: `error.FishyError` if desired state can't be set 73 | """ 74 | if state not in ('Lit', 'Off', 'Blinking'): 75 | raise error.FishyError( 76 | 'Unknown indicator state %s, ID %s' % (state, identity)) 77 | 78 | self._indicators[identity] = state 79 | -------------------------------------------------------------------------------- /sushy_tools/emulator/resources/managers.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from sushy_tools.emulator.resources import base 14 | from sushy_tools import error 15 | 16 | 17 | class FakeDriver(base.DriverBase): 18 | """Redfish manager that copied systems.""" 19 | 20 | def __init__(self, config, logger, systems, chassis): 21 | super().__init__(config, logger) 22 | self._systems = systems 23 | self._chassis = chassis 24 | 25 | def get_manager(self, identity): 26 | """Get a manager by its identity 27 | 28 | :returns: Redfish manager UUID. 29 | :raises: NotFound if the manager cannot be found 30 | """ 31 | try: 32 | system_uuid = self._systems.uuid(identity) 33 | system_name = self._systems.name(identity) 34 | except error.AliasAccessError: 35 | raise 36 | except error.NotFound: 37 | # Re-raise hiding the fact that managers are backed by systems 38 | msg = 'Manager with UUID %s was not found' % identity 39 | self._logger.error(msg) 40 | raise error.NotFound(msg) 41 | else: 42 | result = {'Id': system_uuid, 43 | 'UUID': system_uuid, 44 | 'Name': '%s-Manager' % system_name} 45 | self._logger.debug('Found manager %(mgr)s by UUID %(id)s', 46 | {'mgr': result, 'id': identity}) 47 | return result 48 | 49 | @property 50 | def driver(self): 51 | """Return human-friendly driver information 52 | 53 | :returns: driver information as `str` 54 | """ 55 | return '' 56 | 57 | @property 58 | def managers(self): 59 | """Return available Redfish managers 60 | 61 | :returns: list of UUIDs representing the managers 62 | """ 63 | return sorted(self._systems.systems) 64 | 65 | def get_managed_systems(self, manager): 66 | """Get systems managed by this manager. 67 | 68 | :param manager: Redfish manager object. 69 | :returns: List of Redfish system UUIDs. 70 | """ 71 | return [manager['UUID']] 72 | 73 | def get_managed_chassis(self, manager): 74 | """Get chassis managed by this manager. 75 | 76 | :param manager: Redfish manager object. 77 | :returns: List of Redfish chassis UUIDs. 78 | """ 79 | if manager['UUID'] == self.managers[0]: 80 | return self._chassis.chassis 81 | else: 82 | return [] 83 | 84 | def get_managers_for_system(self, ident): 85 | """Get managers that manage the given system. 86 | 87 | :param ident: System UUID. 88 | :returns: list of UUIDs representing the managers 89 | """ 90 | return [self._systems.uuid(ident)] 91 | -------------------------------------------------------------------------------- /sushy_tools/emulator/resources/storage.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import uuid 17 | 18 | from sushy_tools.emulator.resources import base 19 | from sushy_tools import error 20 | 21 | 22 | class StaticDriver(base.DriverBase): 23 | """Redfish storage backed by configuration file""" 24 | 25 | def __init__(self, config, logger): 26 | super().__init__(config, logger) 27 | self._storage = self._config.get('SUSHY_EMULATOR_STORAGE', {}) 28 | 29 | @property 30 | def driver(self): 31 | """Return human-friendly driver information 32 | 33 | :returns: driver information as `str` 34 | """ 35 | return '' 36 | 37 | def get_storage_col(self, identity): 38 | try: 39 | uu_identity = str(uuid.UUID(identity)) 40 | 41 | return self._storage[uu_identity] 42 | 43 | except KeyError: 44 | msg = ('Error finding storage collection by UUID ' 45 | '"%(identity)s"' % {'identity': identity}) 46 | 47 | self._logger.debug(msg) 48 | 49 | raise error.FishyError(msg) 50 | 51 | def get_all_storage(self): 52 | """Returns all storage instances represented as tuples in the format: 53 | 54 | (System_ID, Storage_ID) 55 | 56 | :returns: list of tuples representing the storage instances 57 | """ 58 | return [(k, st["Id"]) for k in self._storage 59 | for st in self._storage[k]] 60 | -------------------------------------------------------------------------------- /sushy_tools/emulator/resources/systems/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/emulator/resources/systems/__init__.py -------------------------------------------------------------------------------- /sushy_tools/emulator/resources/volumes.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import uuid 17 | 18 | from sushy_tools.emulator import memoize 19 | from sushy_tools.emulator.resources import base 20 | 21 | 22 | class StaticDriver(base.DriverBase): 23 | """Redfish Volumes emulated in libvirt backed by the config file 24 | 25 | Maintains the libvirt volumes in memory. 26 | """ 27 | 28 | def __init__(self, config, logger): 29 | super().__init__(config, logger) 30 | self._volumes = memoize.PersistentDict() 31 | self._volumes.make_permanent( 32 | self._config.get('SUSHY_EMULATOR_STATE_DIR'), 'volumes') 33 | 34 | self._volumes.update( 35 | self._config.get('SUSHY_EMULATOR_VOLUMES', {})) 36 | 37 | @property 38 | def driver(self): 39 | """Return human-friendly driver information 40 | 41 | :returns: driver information as `str` 42 | """ 43 | return '' 44 | 45 | def get_volumes_col(self, identity, storage_id): 46 | try: 47 | uu_identity = str(uuid.UUID(identity)) 48 | 49 | return self._volumes[(uu_identity, storage_id)] 50 | 51 | except (KeyError, ValueError): 52 | msg = ('Error finding volume collection by System UUID %s ' 53 | 'and Storage ID %s' % (uu_identity, storage_id)) 54 | self._logger.debug(msg) 55 | 56 | def add_volume(self, uu_identity, storage_id, vol): 57 | if not self._volumes[(uu_identity, storage_id)]: 58 | self._volumes[(uu_identity, storage_id)] = [] 59 | 60 | vol_col = self._volumes[(uu_identity, storage_id)] 61 | vol_col.append(vol) 62 | self._volumes.update({(uu_identity, storage_id): vol_col}) 63 | 64 | def delete_volume(self, uu_identity, storage_id, vol): 65 | try: 66 | vol_col = self._volumes[(uu_identity, storage_id)] 67 | except KeyError: 68 | msg = ('Error finding volume collection by System UUID %s ' 69 | 'and Storage ID %s' % (uu_identity, storage_id)) 70 | self._logger.debug(msg) 71 | else: 72 | vol_col.remove(vol) 73 | self._volumes.update({(uu_identity, storage_id): vol_col}) 74 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/bios.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#Bios.v1_0_0.Bios", 3 | "Id": "BIOS", 4 | "Name": "BIOS Configuration Current Settings", 5 | "AttributeRegistry": "BiosAttributeRegistryP89.v1_0_0", 6 | "Attributes": {{ bios_current_attributes }}, 7 | "@Redfish.Settings": { 8 | "@odata.type": "#Settings.v1_0_0.Settings", 9 | "SettingsObject": { 10 | "@odata.id": {{ "/redfish/v1/Systems/%s/BIOS/Settings"|format(identity)|tojson }} 11 | } 12 | }, 13 | "Actions": { 14 | "#Bios.ResetBios": { 15 | "target": {{ "/redfish/v1/Systems/%s/BIOS/Actions/Bios.ResetBios"|format(identity)|tojson }} 16 | }, 17 | "#Bios.ChangePassword": { 18 | "target": {{ "/redfish/v1/Systems/%s/BIOS/Actions/Bios.ChangePassword"|format(identity)|tojson }} 19 | } 20 | }, 21 | "@odata.context": "/redfish/v1/$metadata#Bios.Bios", 22 | "@odata.id": {{ "/redfish/v1/Systems/%s/BIOS"|format(identity)|tojson }} 23 | } 24 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/bios_attribute_registry_file.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.context": "/redfish/v1/$metadata#MessageRegistryFile.MessageRegistryFile", 3 | "@odata.id": "/redfish/v1/Registries/BiosAttributeRegistry.v1_0_0", 4 | "@odata.type": "#MessageRegistryFile.v1_1_0.MessageRegistryFile", 5 | "Description": "BIOS Attribute Registry File locations", 6 | "Id": "BiosAttributeRegistry.v1_0_0", 7 | "Languages": [ 8 | "en" 9 | ], 10 | "Location": [ 11 | { 12 | "Language": "en", 13 | "Uri": "/redfish/v1/Systems/Bios/BiosRegistry" 14 | } 15 | ], 16 | "Name": "BIOS Attribute Registry File", 17 | "Oem": { 18 | }, 19 | "Registry": "BiosAttributeRegistry1.0" 20 | } 21 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/bios_registry.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.context": "/redfish/v1/$metadata#AttributeRegistry.AttributeRegistry", 3 | "@odata.id": "/redfish/v1/Systems/Bios/BiosRegistry", 4 | "@odata.type": "#AttributeRegistry.v1_1_1.AttributeRegistry", 5 | "Description": "This registry defines a representation of BIOS Attribute instances", 6 | "Id": "BiosAttributeRegistryP89.v1_0_0", 7 | "Language": "en", 8 | "Name": "BIOS Attribute Registry", 9 | "OwningEntity": "VendorA", 10 | "RegistryVersion": "1.1.1", 11 | "RegistryEntries": { 12 | "Attributes": [ 13 | { 14 | "AttributeName": "ProcTurboMode", 15 | "CurrentValue": null, 16 | "DisplayName": "Turbo Boost", 17 | "HelpText": "Governs the Turbo Boost Technology. This feature allows the processor cores to be automatically clocked up in frequency beyond the advertised processor speed.", 18 | "Hidden": false, 19 | "Immutable": false, 20 | "ReadOnly": false, 21 | "Type": "Enumeration", 22 | "Value": [ 23 | { 24 | "ValueDisplayName": "Enabled", 25 | "ValueName": "Enabled" 26 | }, 27 | { 28 | "ValueDisplayName": "Disabled", 29 | "ValueName": "Disabled" 30 | } 31 | ], 32 | "WarningText": null, 33 | "WriteOnly": false 34 | }, 35 | { 36 | "AttributeName": "BootMode", 37 | "CurrentValue": null, 38 | "DisplayName": "Boot Mode", 39 | "HelpText": "This field determines the boot mode of the system.\n\nSelecting 'UEFI' enables booting to Unified Extensible Firmware Interface (UEFI) capable operating systems.\n\nSelecting 'BIOS' (the default) ensures compatibility with operating systems that do not support UEFI.", 40 | "Hidden": false, 41 | "Immutable": false, 42 | "ReadOnly": false, 43 | "Type": "Enumeration", 44 | "Value": [ 45 | { 46 | "ValueDisplayName": "BIOS", 47 | "ValueName": "Bios" 48 | }, 49 | { 50 | "ValueDisplayName": "UEFI", 51 | "ValueName": "Uefi" 52 | } 53 | ], 54 | "WarningText": null, 55 | "WriteOnly": false 56 | }, 57 | { 58 | "AttributeName": "EmbeddedSata", 59 | "CurrentValue": null, 60 | "DisplayName": "Embedded SATA", 61 | "HelpText": "Allows the Embedded SATA to be set to Off, ATA, AHCI or RAID Mode.", 62 | "Hidden": false, 63 | "Immutable": false, 64 | "ReadOnly": false, 65 | "Type": "Enumeration", 66 | "Value": [ 67 | { 68 | "ValueDisplayName": "ATA Mode", 69 | "ValueName": "Ata" 70 | }, 71 | { 72 | "ValueDisplayName": "AHCI Mode", 73 | "ValueName": "Ahci" 74 | }, 75 | { 76 | "ValueDisplayName": "RAID Mode", 77 | "ValueName": "Raid" 78 | }, 79 | { 80 | "ValueDisplayName": "Off", 81 | "ValueName": "Off" 82 | } 83 | ], 84 | "WarningText": null, 85 | "WriteOnly": false 86 | }, 87 | { 88 | "AttributeName": "L2Cache", 89 | "DisplayName": "Serial Number", 90 | "HelpText": "This field displays the amount of memory in the corresponding processor cache.", 91 | "ReadOnly": false, 92 | "MaxLength": 16, 93 | "MinLength": 0, 94 | "Type": "String", 95 | "CurrentValue": null, 96 | "IsSystemUniqueProperty": false 97 | }, 98 | { 99 | "AttributeName": "NicBoot1", 100 | "DisplayName": "Embedded NIC 1 Boot", 101 | "HelpText": "Use this option to enable or disable network boot (PXE, iSCSI, FCoE or UEFI HTTP) for the selected NIC. You might need to configure the NIC firmware for the boot option to be active.", 102 | "ReadOnly": false, 103 | "Type": "Enumeration", 104 | "CurrentValue": null, 105 | "Value": [ 106 | { 107 | "ValueName": "NetworkBoot", 108 | "ValueDisplayName": "Network Boot" 109 | }, 110 | { 111 | "ValueName": "Disabled", 112 | "ValueDisplayName": "Disabled" 113 | } 114 | ] 115 | }, 116 | { 117 | "AttributeName": "NumCores", 118 | "DisplayName": "Number Cores", 119 | "HelpText": "This field displays the number of cores in the processor package.", 120 | "ReadOnly": true, 121 | "LowerBound": 10, 122 | "UpperBound": 20, 123 | "Type": "Integer", 124 | "CurrentValue": null, 125 | "IsSystemUniqueProperty": false 126 | }, 127 | { 128 | "AttributeName": "QuietBoot", 129 | "DisplayName": "Quiet Boot", 130 | "HelpText": "Set the option for Quiet Boot.", 131 | "ReadOnly": false, 132 | "Type": "Boolean", 133 | "CurrentValue": null, 134 | "IsSystemUniqueProperty": false 135 | }, 136 | { 137 | "AttributeName": "SecureBootStatus", 138 | "DisplayName": "Secure Boot Status", 139 | "HelpText": "The current state of Secure Boot configuration.", 140 | "ReadOnly": true, 141 | "Immutable": true, 142 | "Type": "Enumeration", 143 | "CurrentValue": null, 144 | "Value": [ 145 | { 146 | "ValueName": "Enabled", 147 | "ValueDisplayName": "Enabled" 148 | }, 149 | { 150 | "ValueName": "Disabled", 151 | "ValueDisplayName": "Disabled" 152 | } 153 | ] 154 | }, 155 | { 156 | "AttributeName": "SerialNumber", 157 | "DisplayName": "Serial Number", 158 | "HelpText": "Use this option to set the system serial number.", 159 | "ReadOnly": false, 160 | "MaxLength": 16, 161 | "MinLength": 0, 162 | "Type": "String", 163 | "CurrentValue": null, 164 | "IsSystemUniqueProperty": true 165 | }, 166 | { 167 | "AttributeName": "SysPassword", 168 | "DisplayName": "System Password", 169 | "HelpText": "The system password is the password that must be entered to allow the system to boot to an operating system.", 170 | "ReadOnly": false, 171 | "Type": "Password", 172 | "CurrentValue": null, 173 | "IsSystemUniqueProperty": false 174 | } 175 | ] 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/bios_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#Bios.v1_0_0.Bios", 3 | "Id": "Settings", 4 | "Name": "BIOS Configuration Pending Settings", 5 | "AttributeRegistry": "BiosAttributeRegistryP89.v1_0_0", 6 | "Attributes": {{ bios_pending_attributes }}, 7 | "@odata.context": "/redfish/v1/$metadata#Bios.Bios", 8 | "@odata.id": {{ "/redfish/v1/Systems/%s/BIOS/Settings"|format(identity)|tojson }}, 9 | "@Redfish.Copyright": "Copyright 2014-2016 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 10 | } 11 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/certificate.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#Certificate.v1_3_0.Certificate", 3 | "Id": {{ cert_id|string|tojson }}, 4 | "Name": "HTTPS Certificate {{ cert_id }}", 5 | "CertificateString": {{ cert_string|string|tojson }}, 6 | "CertificateType": {{ cert_type|string|tojson }}, 7 | "Oem": {}, 8 | "@odata.id": {{ location|string|tojson }}, 9 | "@Redfish.Copyright": "Copyright 2014-2021 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 10 | } 11 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/certificate_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#CertificateCollection.CertificateCollection", 3 | "Name": "Certificate Collection", 4 | "Members@odata.count": {{ certificates|length }}, 5 | "Members": [ 6 | {% for item in certificates %} 7 | { 8 | "@odata.id": {{ "%s/%s"|format(location, item)|tojson }} 9 | }{% if not loop.last %},{% endif %} 10 | {% endfor %} 11 | ], 12 | "@Redfish.SupportedCertificates": [ 13 | "PEM" 14 | ], 15 | "Oem": {}, 16 | "@odata.id": {{ location|tojson }}, 17 | "@Redfish.Copyright": "Copyright 2014-2021 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 18 | } 19 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/certificate_locations.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#CertificateLocations.v1_0_2.CertificateLocations", 3 | "Id": "CertificateLocations", 4 | "Name": "Certificate Locations", 5 | "Links": { 6 | "Certificates": [ 7 | {% for item in certificates %} 8 | { 9 | "@odata.id": {{ item|tojson }} 10 | }{% if not loop.last %},{% endif %} 11 | {% endfor %} 12 | ] 13 | }, 14 | "Oem": {}, 15 | "@odata.id": "/redfish/v1/CertificateService/CertificateLocations", 16 | "@Redfish.Copyright": "Copyright 2014-2021 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 17 | } 18 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/certificate_service.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#CertificateService.v1_0_4.CertificateService", 3 | "Id": "CertificateService", 4 | "Name": "Certificate Service", 5 | "Actions": { 6 | "#CertificateService.ReplaceCertificate": { 7 | "target": "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate", 8 | "@Redfish.ActionInfo": "/redfish/v1/CertificateService/ReplaceCertificateActionInfo" 9 | } 10 | }, 11 | "CertificateLocations": { 12 | "@odata.id": "/redfish/v1/CertificateService/CertificateLocations" 13 | }, 14 | "Oem": {}, 15 | "@odata.id": "/redfish/v1/CertificateService", 16 | "@Redfish.Copyright": "Copyright 2014-2021 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 17 | } 18 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/chassis.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#Chassis.v1_5_0.Chassis", 3 | "Id": {{ identity|string|tojson }}, 4 | "Name": {{ name|string|tojson }}, 5 | "UUID": {{ uuid|string|tojson }}, 6 | "AssetTag": "Chicago-45Z-2381", 7 | "ChassisType": "Enclosure", 8 | "Manufacturer": "Contoso", 9 | "Model": "3500RX", 10 | "SKU": "8675309", 11 | "SerialNumber": "437XR1138R2", 12 | "PartNumber": "224071-J23", 13 | "PowerState": "On", 14 | {%- if indicator_led %} 15 | "IndicatorLED": {{ indicator_led|string|tojson }}, 16 | {%- endif %} 17 | "HeightMm": 44.45, 18 | "WidthMm": 431.8, 19 | "DepthMm": 711, 20 | "WeightKg": 15.31, 21 | "Thermal": { 22 | "@odata.id": {{ "/redfish/v1/Chassis/%s/Thermal"|format(identity)|tojson }} 23 | }, 24 | "Location": { 25 | "PostalAddress": { 26 | "Country": "US", 27 | "Territory": "OR", 28 | "City": "Portland", 29 | "Street": "1001 SW 5th Avenue", 30 | "HouseNumber": 1100, 31 | "Name": "Distributed Management Task Force, Inc.", 32 | "PostalCode": "97204" 33 | }, 34 | "Placement": { 35 | "Row": "North", 36 | "Rack": "WEB43", 37 | "RackOffsetUnits": "EIA_310", 38 | "RackOffset": 12 39 | } 40 | }, 41 | "Status": { 42 | "State": "Enabled", 43 | "Health": "OK" 44 | }, 45 | "Links": { 46 | "ComputerSystems": [ 47 | {%- for system in contained_systems %} 48 | { 49 | "@odata.id": {{ "/redfish/v1/Systems/%s"|format(system)|tojson }} 50 | }{% if not loop.last %},{% endif %} 51 | {% endfor -%} 52 | ], 53 | {% if contained_by %} 54 | "ContainedBy": {{ contained_by|string|tojson }}, 55 | {% endif -%} 56 | {% if contained_chassis %} 57 | "Contains": [ 58 | {%- for chassis in contained_chassis %} 59 | { 60 | "@odata.id": {{ "/redfish/v1/Chassis/%s"|format(chassis)|tojson }} 61 | }{% if not loop.last %},{% endif %} 62 | {% endfor -%} 63 | ], 64 | {% endif -%} 65 | {% if storage %} 66 | "Storage": [ 67 | {%- for stg in storage %} 68 | { 69 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage/%s"|format(stg[0], stg[1])|tojson }} 70 | }{% if not loop.last %},{% endif %} 71 | {% endfor -%} 72 | ], 73 | {% endif -%} 74 | {% if drives %} 75 | "Drives": [ 76 | {%- for drive in drives %} 77 | { 78 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage/%s/Drives/%s"|format(drive[0], drive[1], drive[2])|tojson }} 79 | }{% if not loop.last %},{% endif %} 80 | {% endfor -%} 81 | ], 82 | {% endif -%} 83 | "ManagedBy": [ 84 | {%- for manager in managers %} 85 | { 86 | "@odata.id": {{ "/redfish/v1/Managers/%s"|format(manager)|tojson }} 87 | }{% if not loop.last %},{% endif %} 88 | {% endfor -%} 89 | ], 90 | "ManagersInChassis": [ 91 | {%- for manager in contained_managers %} 92 | { 93 | "@odata.id": {{ "/redfish/v1/Managers/%s"|format(manager)|tojson }} 94 | }{% if not loop.last %},{% endif %} 95 | {% endfor -%} 96 | ] 97 | }, 98 | "@odata.context": "/redfish/v1/$metadata#Chassis.Chassis", 99 | "@odata.id": {{ "/redfish/v1/Chassis/%s"|format(identity)|string|tojson }}, 100 | "@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 101 | } -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/chassis_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#ChassisCollection.ChassisCollection", 3 | "Name": "Chassis Collection", 4 | "Members@odata.count": {{ chassis|length }}, 5 | "Members": [ 6 | {% for ch in chassis %} 7 | { 8 | "@odata.id": {{ "/redfish/v1/Chassis/%s"|format(ch)|tojson }} 9 | }{% if not loop.last %},{% endif %} 10 | {% endfor %} 11 | ], 12 | "@odata.context": "/redfish/v1/$metadata#ChassisCollection.ChassisCollection", 13 | "@odata.id": "/redfish/v1/Chassis", 14 | "@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 15 | } -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/drive.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#Drive.v1_4_0.Drive", 3 | "Id": {{ drive['Id']|string|tojson }}, 4 | "Name": {{ drive['Name']|string|tojson }}, 5 | "IndicatorLED": "Lit", 6 | "Model": "C123", 7 | "Revision": "100A", 8 | "Status": { 9 | "@odata.type": "#Resource.Status", 10 | "State": "Enabled", 11 | "Health": "OK" 12 | }, 13 | "CapacityBytes": {{ drive['CapacityBytes'] }}, 14 | "FailurePredicted": false, 15 | "Protocol": {{ drive['Protocol']|string|tojson }}, 16 | "MediaType": "HDD", 17 | "Manufacturer": "Contoso", 18 | "SerialNumber": "1234570", 19 | "PartNumber": "C123-1111", 20 | "HotspareType": "Global", 21 | "EncryptionAbility": "SelfEncryptingDrive", 22 | "EncryptionStatus": "Unlocked", 23 | "RotationSpeedRPM": 15000, 24 | "BlockSizeBytes": 512, 25 | "CapableSpeedGbs": 12, 26 | "NegotiatedSpeedGbs": 12, 27 | "@odata.context": "/redfish/v1/$metadata#Drive.Drive", 28 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage/%s/Drives/%s"|format(identity, storage_id, drive['Id'])|tojson }}, 29 | "@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 30 | } 31 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": { 3 | "code": "Base.1.0.GeneralError", 4 | "message": {{ message|string|tojson }}, 5 | "@Message.ExtendedInfo": [ 6 | { 7 | "@odata.type": "/redfish/v1/$metadata#Message.1.0.0.Message", 8 | "MessageId": "Base.1.0.GeneralError" 9 | } 10 | ] 11 | } 12 | } -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/ethernet_interface.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#EthernetInterface.v1_0_2.EthernetInterface", 3 | "Id": {{ nic['id']|string|tojson }}, 4 | "Name": {{ "VNIC %s"|format(nic['id'])|tojson }}, 5 | "Status": { 6 | "State": "Enabled", 7 | "Health": "OK" 8 | }, 9 | "PermanentMACAddress": {{ nic['mac']|string|tojson }}, 10 | "MACAddress": {{ nic['mac']|string|tojson }}, 11 | "@odata.context": "/redfish/v1/$metadata#EthernetInterface.EthernetInterface", 12 | "@odata.id": {{ "/redfish/v1/Systems/%s/EthernetInterfaces/%s"|format(identity, nic['id'])|tojson }} 13 | } 14 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/ethernet_interfaces_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#EthernetInterfaceCollection.EthernetInterfaceCollection", 3 | "Name": "Ethernet Interface Collection", 4 | "Description": "Virtual NICs", 5 | "Members@odata.count": {{ nics|length }}, 6 | "Members": [ 7 | {% for nic in nics %} 8 | { 9 | "@odata.id": {{ "/redfish/v1/Systems/%s/EthernetInterfaces/%s"|format(identity, nic.id)|tojson }} 10 | }{% if not loop.last %},{% endif %} 11 | {% endfor %} 12 | ], 13 | "Oem": {}, 14 | "@odata.context": "/redfish/v1/$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection", 15 | "@odata.id": {{ "/redfish/v1/Systems/%s/EthernetInterfaces"|format(identity)|tojson }} 16 | } 17 | 18 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/manager_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#ManagerCollection.ManagerCollection", 3 | "Name": "Manager Collection", 4 | "Members@odata.count": {{ manager_count }}, 5 | "Members": [ 6 | {% for manager in managers %} 7 | { 8 | "@odata.id": {{ "/redfish/v1/Managers/%s"|format(manager)|tojson }} 9 | }{% if not loop.last %},{% endif %} 10 | {% endfor %} 11 | ], 12 | "Oem": {}, 13 | "@odata.context": "/redfish/v1/$metadata#ManagerCollection.ManagerCollection", 14 | "@odata.id": "/redfish/v1/Managers", 15 | "@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 16 | } 17 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/message_registry.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.context" : "/redfish/v1/$metadata#MessageRegistry.MessageRegistry", 3 | "@odata.id" : "/redfish/v1/Registries/Messages/Registry", 4 | "@odata.type" : "#MessageRegistry.v1_1_1.MessageRegistry", 5 | "Id": "1.1.1", 6 | "Name": "Message Registry", 7 | "Language": "en", 8 | "RegistryVersion": "1.1.1", 9 | "Description": "This registry defines the messages for Redfish", 10 | "RegistryPrefix": "Msg", 11 | "OwningEntity": "VendorA", 12 | "Messages": { 13 | "AMP0300": { 14 | "Description": "None.", 15 | "Message": "The system board %1 current is less than the lower warning threshold.", 16 | "Severity": "Warning", 17 | "NumberOfArgs": 1, 18 | "ParamTypes": [ 19 | "string" 20 | ], 21 | "Resolution": "Review system power policy and review system configuration changes." 22 | }, 23 | "BIOS001": { 24 | "Description": "The command was successful.", 25 | "Message": "The command was successful", 26 | "Severity": "Informational", 27 | "NumberOfArgs": 0, 28 | "Resolution": "No response action is required." 29 | }, 30 | "BIOS002": { 31 | "Description": "Unable to allocate required memory to perform the requested operation", 32 | "Message": "Resource allocation failure.", 33 | "Severity": "Critical", 34 | "NumberOfArgs": 0, 35 | "Resolution": "Power cycle system." 36 | }, 37 | "BIOS003": { 38 | "Description": "An invalid number of arguments was passed to the method.", 39 | "Message": "Missing required parameter. Refer to the inserted comment.", 40 | "Severity": "Warning", 41 | "NumberOfArgs": 0, 42 | "Resolution": "Enter all the required command parameters. Check documentation and try again." 43 | }, 44 | "BIOS004": { 45 | "Description": "The value for the specified parameter is invalid.", 46 | "Message": "Invalid parameter value for %1", 47 | "Severity": "Warning", 48 | "NumberOfArgs": 1, 49 | "ParamTypes": [ 50 | "string" 51 | ], 52 | "Resolution": "Verify that parameter values passed to the method are entered as they appear in the enumeration and parameter data type matches the documentation." 53 | }, 54 | "BIOS005": { 55 | "Description": "The number of AttributeName and AttributeValue parameters do not match.", 56 | "Message": "Mismatch in AttributeName and AttributeValue count", 57 | "Severity": "Warning", 58 | "NumberOfArgs": 0, 59 | "Resolution": "Enter the same number of parameters for AttributeName and AttributeValue. Refer to documentation for method input parameter details." 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/message_registry_file.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.context": "/redfish/v1/$metadata#MessageRegistryFile.MessageRegistryFile", 3 | "@odata.id": "/redfish/v1/Registries/Messages", 4 | "@odata.type": "#MessageRegistryFile.v1_1_1.MessageRegistryFile", 5 | "Description": "Message Registry File locations", 6 | "Id": "Messages", 7 | "Languages": [ 8 | "En" 9 | ], 10 | "Languages@odata.count": 1, 11 | "Location": [ 12 | { 13 | "Language": "En", 14 | "Uri": "/redfish/v1/Registries/Messages/Registry" 15 | } 16 | ], 17 | "Location@odata.count": 1, 18 | "Name": "Message Registry File", 19 | "Registry": "Base.1.0" 20 | } 21 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/processor.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#Processor.v1_0_7.Processor", 3 | "Id": {{ processor['id']|string|tojson }}, 4 | "Name": "Processor", 5 | "Socket": {{ processor['socket']|string|tojson }}, 6 | "ProcessorType": "CPU", 7 | "ProcessorArchitecture": "x86", 8 | "InstructionSet": "x86-64", 9 | "Manufacturer": {{ processor['vendor']|string|tojson }}, 10 | "Model": {{ processor['model']|string|tojson }}, 11 | "TotalCores": {{ processor['cores']|string|tojson }}, 12 | "TotalThreads": {{ processor['threads']|string|tojson }}, 13 | "Status": { 14 | "@odata.type": "#Resource.Status", 15 | "State": "Enabled", 16 | "Health": "OK" 17 | }, 18 | "@odata.context": "/redfish/v1/$metadata#Processor.Processor", 19 | "@odata.id": {{ "/redfish/v1/Systems/%s/Processors/%s"|format(identity, processor['Id'])|tojson }}, 20 | "@Redfish.Copyright": "Copyright 2014-2019 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright" 21 | } 22 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/processors_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#ProcessorCollection.ProcessorCollection", 3 | "Name": "Processors Collection", 4 | "Members@odata.count": {{ processors|length}}, 5 | "Members": [ 6 | {% for processor in processors %} 7 | { 8 | "@odata.id": {{ "/redfish/v1/Systems/%s/Processors/%s"|format(identity, processor.id)|tojson }} 9 | }{% if not loop.last %},{% endif %} 10 | {% endfor %} 11 | ], 12 | "@odata.context": "/redfish/v1/$metadata#ProcessorCollection.ProcessorCollection", 13 | "@odata.id": {{ "/redfish/v1/Systems/%s/Processors/%s"|format(identity, processor)|tojson }}, 14 | "@Redfish.Copyright": "Copyright 2014-2020 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright" 15 | } -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/registry_file_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.context": "/redfish/v1/$metadata#MessageRegistryFileCollection.MessageRegistryFileCollection", 3 | "@odata.id": "/redfish/v1/Registries", 4 | "@odata.type": "#MessageRegistryFileCollection.MessageRegistryFileCollection", 5 | "Description": "Registry Repository", 6 | "Members": [ 7 | { 8 | "@odata.id": "/redfish/v1/Registries/Messages" 9 | }, 10 | { 11 | "@odata.id": "/redfish/v1/Registries/BiosAttributeRegistry.v1_0_0" 12 | } 13 | ], 14 | "Members@odata.count": 2, 15 | "Name": "Registry File Collection" 16 | } 17 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/root.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#ServiceRoot.v1_5_0.ServiceRoot", 3 | "Id": "RedvirtService", 4 | "Name": "Redvirt Service", 5 | "RedfishVersion": "1.5.0", 6 | "UUID": "85775665-c110-4b85-8989-e6162170b3ec", 7 | {% if feature_set == "full" %} 8 | "Chassis": { 9 | "@odata.id": "/redfish/v1/Chassis" 10 | }, 11 | {% endif %} 12 | "Systems": { 13 | "@odata.id": "/redfish/v1/Systems" 14 | }, 15 | {% if feature_set != "minimum" %} 16 | "Managers": { 17 | "@odata.id": "/redfish/v1/Managers" 18 | }, 19 | {% endif %} 20 | {% if feature_set == "full" %} 21 | "Registries": { 22 | "@odata.id": "/redfish/v1/Registries" 23 | }, 24 | "CertificateService": { 25 | "@odata.id": "/redfish/v1/CertificateService" 26 | }, 27 | "UpdateService": { 28 | "@odata.id": "/redfish/v1/UpdateService" 29 | }, 30 | {% endif %} 31 | "@odata.id": "/redfish/v1/", 32 | "@Redfish.Copyright": "Copyright 2014-2016 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 33 | } 34 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/secure_boot.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#SecureBoot.v1_1_0.SecureBoot", 3 | "Id": "SecureBoot", 4 | "Name": "UEFI Secure Boot", 5 | "Actions": {}, 6 | "SecureBootEnable": {{ 'true' if secure_boot_enable else 'false' }}, 7 | "SecureBootCurrentBoot": {{ secure_boot_current_boot|string|tojson }}, 8 | "SecureBootMode": "DeployedMode", 9 | "@odata.id": {{ "/redfish/v1/Systems/%s/SecureBoot"|format(identity)|tojson }}, 10 | "@odata.context": "/redfish/v1/$metadata#SecureBoot.SecureBoot", 11 | "@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 12 | } 13 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/simple_storage.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#SimpleStorage.v1_2_0.SimpleStorage", 3 | "Id": {{ simple_storage['Id']|string|tojson }}, 4 | "Name": {{ "%s Controller"|format(simple_storage['Name'])|tojson }}, 5 | "Devices": [ 6 | {% for device in simple_storage['DeviceList'] %} 7 | { 8 | "@odata.type": "#SimpleStorage.v1_1_0.Device", 9 | "Name": {{ device['Name']|string|tojson }}, 10 | "CapacityBytes": {{ device['CapacityBytes'] }}, 11 | "Status": { 12 | "@odata.type": "#Resource.Status", 13 | "State": "Enabled", 14 | "Health": "OK" 15 | } 16 | }{% if not loop.last %},{% endif %} 17 | {% endfor %} 18 | ], 19 | "@odata.context": "/redfish/v1/$metadata#SimpleStorage.SimpleStorage", 20 | "@odata.id": {{ "/redfish/v1/Systems/%s/SimpleStorage/%s"|format(identity, simple_storage['Id'])|tojson }} 21 | } -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/simple_storage_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#SimpleStorageCollection.SimpleStorageCollection", 3 | "Name": "Simple Storage Collection", 4 | "Members@odata.count": {{ simple_storage_controllers|length }}, 5 | "Members": [ 6 | {% for simple_storage in simple_storage_controllers %} 7 | { 8 | "@odata.id": {{ "/redfish/v1/Systems/%s/SimpleStorage/%s"|format(identity, simple_storage_controllers[simple_storage]['Id'])|tojson }} 9 | }{% if not loop.last %},{% endif %} 10 | {% endfor %} 11 | ], 12 | "Oem": {}, 13 | "@odata.context": "/redfish/v1/$metadata#SimpleStorageCollection.SimpleStorageCollection", 14 | "@odata.id": {{ "/redfish/v1/Systems/%s/SimpleStorage"|format(identity)|tojson }} 15 | } 16 | 17 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/storage.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "##Storage.v1_4_0.Storage", 3 | "Id": {{ storage['Id']|string|tojson }}, 4 | "Name": {{ storage['Name']|string|tojson }}, 5 | "Status": { 6 | "@odata.type": "#Resource.Status", 7 | "State": "Enabled", 8 | "Health": "OK", 9 | "HealthRollup": "OK" 10 | }, 11 | {% if storage['StorageControllers'] %} 12 | "StorageControllers": [ 13 | {%- for ctl in storage['StorageControllers'] %} 14 | { 15 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage/%s#/StorageControllers/%s"|format(identity, storage['Id'], ctl['MemberId'])|tojson }}, 16 | "@odata.type": "#Storage.v1_3_0.StorageController", 17 | "MemberId": {{ ctl['MemberId']|string|tojson }}, 18 | "Name": {{ ctl['Name']|string|tojson }}, 19 | "Status": { 20 | "@odata.type": "#Resource.Status", 21 | "State": "Enabled", 22 | "Health": "OK" 23 | }, 24 | "Manufacturer": "Contoso", 25 | "Model": "12Gbs Integrated RAID", 26 | "SerialNumber": "2M220100SL", 27 | "PartNumber": "CT18754", 28 | "SpeedGbps": {{ ctl['SpeedGbps'] }}, 29 | "FirmwareVersion": "1.0.0.7", 30 | "SupportedControllerProtocols": [ 31 | "PCIe" 32 | ], 33 | "SupportedDeviceProtocols": [ 34 | "SAS", 35 | "SATA" 36 | ] 37 | }{% if not loop.last %},{% endif %} 38 | {% endfor -%} 39 | ], 40 | {% endif -%} 41 | {% if storage['Drives'] %} 42 | "Drives": [ 43 | {%- for drive in storage['Drives'] %} 44 | { 45 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage/%s/Drives/%s"|format(identity, storage['Id'], drive)|tojson }} 46 | }{% if not loop.last %},{% endif %} 47 | {% endfor -%} 48 | ], 49 | {% endif -%} 50 | "Volumes": { 51 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage/%s/Volumes"|format(identity, storage['Id'])|tojson }} 52 | }, 53 | "@odata.context": "/redfish/v1/$metadata#Storage.Storage", 54 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage/%s"|format(identity, storage['Id'])|tojson }} 55 | } 56 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/storage_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#StorageCollection.StorageCollection", 3 | "Name": "Storage Collection", 4 | "Members@odata.count": {{ storage_col|length }}, 5 | "Members": [ 6 | {% for storage in storage_col %} 7 | { 8 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage/%s"|format(identity, storage.Id)|tojson }} 9 | }{% if not loop.last %},{% endif %} 10 | {% endfor %} 11 | ], 12 | "Oem": {}, 13 | "@odata.context": "/redfish/v1/$metadata#StorageCollection.StorageCollection", 14 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage"|format(identity)|tojson }} 15 | } 16 | 17 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/system.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#ComputerSystem.v1_13_0.ComputerSystem", 3 | "Id": {{ identity|string|tojson }}, 4 | "Name": {{ name|string|tojson }}, 5 | "UUID": {{ uuid|string|tojson }}, 6 | {%- if feature_set == "full" %} 7 | "Manufacturer": "Sushy Emulator", 8 | "Status": { 9 | "State": "Enabled", 10 | "Health": "OK", 11 | "HealthRollUp": "OK" 12 | }, 13 | {% endif %} 14 | {%- if power_state %} 15 | "PowerState": {{ power_state|string|tojson }}, 16 | {%- endif %} 17 | "Boot": { 18 | {%- if boot_source_target %} 19 | "BootSourceOverrideEnabled": "Continuous", 20 | "BootSourceOverrideTarget": {{ boot_source_target|string|tojson }}, 21 | "BootSourceOverrideTarget@Redfish.AllowableValues": [ 22 | "Pxe", 23 | "Cd", 24 | {%- if boot_source_mode %} 25 | {%- if uefi_mode %} 26 | "Hdd", 27 | "UefiHttp" 28 | ], 29 | "BootSourceOverrideMode": {{ boot_source_mode|string|tojson }}, 30 | "UefiTargetBootSourceOverride": "/0x31/0x33/0x01/0x01", 31 | "HttpBootUri": {{ http_boot_uri|string|tojson }} 32 | {%- else %} 33 | "Hdd" 34 | ], 35 | "BootSourceOverrideMode": {{ boot_source_mode|string|tojson }} 36 | {%- endif %} 37 | {%- else %} 38 | "Hdd" 39 | ] 40 | {%- endif %} 41 | {%- else %} 42 | "BootSourceOverrideEnabled": "Continuous" 43 | {%- endif %} 44 | }, 45 | {%- if feature_set == "full" %} 46 | "ProcessorSummary": { 47 | {%- if total_cpus %} 48 | "Count": {{ total_cpus }}, 49 | {%- endif %} 50 | "Status": { 51 | "State": "Enabled", 52 | "Health": "OK", 53 | "HealthRollUp": "OK" 54 | } 55 | }, 56 | "MemorySummary": { 57 | {%- if total_memory_gb %} 58 | "TotalSystemMemoryGiB": {{ total_memory_gb }}, 59 | {%- endif %} 60 | "Status": { 61 | "State": "Enabled", 62 | "Health": "OK", 63 | "HealthRollUp": "OK" 64 | } 65 | }, 66 | "Bios": { 67 | "@odata.id": {{ "/redfish/v1/Systems/%s/BIOS"|format(identity)|tojson }} 68 | }, 69 | "BiosVersion": {{ bios_version|string|tojson }}, 70 | "Processors": { 71 | "@odata.id": {{ "/redfish/v1/Systems/%s/Processors"|format(identity)|tojson }} 72 | }, 73 | "Memory": { 74 | "@odata.id": {{ "/redfish/v1/Systems/%s/Memory"|format(identity)|tojson }} 75 | }, 76 | "SecureBoot": { 77 | "@odata.id": {{ "/redfish/v1/Systems/%s/SecureBoot"|format(identity)|tojson }} 78 | }, 79 | "SimpleStorage": { 80 | "@odata.id": {{ "/redfish/v1/Systems/%s/SimpleStorage"|format(identity)|tojson }} 81 | }, 82 | "Storage": { 83 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage"|format(identity)|tojson }} 84 | }, 85 | {%- if indicator_led %} 86 | "IndicatorLED": {{ indicator_led|string|tojson }}, 87 | {%- endif %} 88 | {%- endif %} 89 | {%- if feature_set != "minimum" %} 90 | "EthernetInterfaces": { 91 | "@odata.id": {{ "/redfish/v1/Systems/%s/EthernetInterfaces"|format(identity)|tojson }} 92 | }, 93 | "VirtualMedia": { 94 | "@odata.id": {{ "/redfish/v1/Systems/%s/VirtualMedia"|format(identity)|tojson }} 95 | }, 96 | {%- endif %} 97 | "Links": { 98 | {%- if feature_set == "full" %} 99 | "Chassis": [ 100 | {%- for chassis_ in chassis %} 101 | { 102 | "@odata.id": {{ "/redfish/v1/Chassis/%s"|format(chassis_)|tojson }} 103 | }{% if not loop.last %},{% endif %} 104 | {% endfor -%} 105 | ], 106 | {% endif %} 107 | {%- if feature_set != "minimum" %} 108 | "ManagedBy": [ 109 | {%- for manager in managers %} 110 | { 111 | "@odata.id": {{ "/redfish/v1/Managers/%s"|format(manager)|tojson }} 112 | }{% if not loop.last %},{% endif %} 113 | {% endfor -%} 114 | ] 115 | {% endif %} 116 | }, 117 | "Actions": { 118 | "#ComputerSystem.Reset": { 119 | "target": {{ "/redfish/v1/Systems/%s/Actions/ComputerSystem.Reset"|format(identity)|tojson }}, 120 | "ResetType@Redfish.AllowableValues": [ 121 | "On", 122 | "ForceOff", 123 | "GracefulShutdown", 124 | "GracefulRestart", 125 | "ForceRestart", 126 | "Nmi", 127 | "ForceOn" 128 | ] 129 | } 130 | }, 131 | "@odata.context": "/redfish/v1/$metadata#ComputerSystem.ComputerSystem", 132 | "@odata.id": {{ "/redfish/v1/Systems/%s"|format(identity)|tojson }}, 133 | "@Redfish.Copyright": "Copyright 2014-2016 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 134 | } 135 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/system_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#ComputerSystemCollection.ComputerSystemCollection", 3 | "Name": "Computer System Collection", 4 | "Members@odata.count": {{ system_count }}, 5 | "Members": [ 6 | {% for system in systems %} 7 | { 8 | "@odata.id": {{ "/redfish/v1/Systems/%s"|format(system)|tojson }} 9 | }{% if not loop.last %},{% endif %} 10 | {% endfor %} 11 | ], 12 | "@odata.context": "/redfish/v1/$metadata#ComputerSystemCollection.ComputerSystemCollection", 13 | "@odata.id": "/redfish/v1/Systems", 14 | "@Redfish.Copyright": "Copyright 2014-2016 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 15 | } 16 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type":"#Task.v1_4_3.Task", 3 | "Id":"42", 4 | "Name":"Task 42", 5 | "Description": "Task description", 6 | "TaskMonitor":"/taskmon/42", 7 | "TaskState":"Completed", 8 | "TaskStatus":"OK", 9 | "PercentComplete": 100, 10 | "@odata.id":"/redfish/v1/TaskService/Tasks/42" 11 | } 12 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/task_service.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#TaskService.v1_1_2.TaskService", 3 | "Id": "TaskService", 4 | "Name": "Tasks Service", 5 | "DateTime": "2015-03-13T04:14:33+06:00", 6 | "CompletedTaskOverWritePolicy": "Manual", 7 | "LifeCycleEventOnTaskStateChange": true, 8 | "Status": { 9 | "State": "Enabled", 10 | "Health": "OK" 11 | }, 12 | "ServiceEnabled": true, 13 | "Tasks": { 14 | "@odata.id": "/redfish/v1/TaskService/Tasks" 15 | }, 16 | "Oem": {}, 17 | "@odata.context": "/redfish/v1/$metadata#TaskService.TaskService", 18 | "@odata.id": "/redfish/v1/TaskService" 19 | } 20 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/thermal.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#Thermal.v1_3_0.Thermal", 3 | "Id": "Thermal", 4 | "Name": "Thermal", 5 | "Temperatures": [ 6 | {%- for system in systems %} 7 | { 8 | "@odata.id": {{ "/redfish/v1/Chassis/%s/Thermal#/Temperatures/%d"|format(chassis, loop.index0)|tojson }}, 9 | "MemberId": {{ loop.index0|string|tojson }}, 10 | "Name": "CPU Temp", 11 | "SensorNumber": {{ loop.index0|string|tojson }}, 12 | "Status": { 13 | "State": "Enabled", 14 | "Health": "OK" 15 | }, 16 | "ReadingCelsius": 41, 17 | "UpperThresholdNonCritical": 42, 18 | "UpperThresholdCritical": 45, 19 | "UpperThresholdFatal": 48, 20 | "MinReadingRangeTemp": 0, 21 | "MaxReadingRangeTemp": 60, 22 | "PhysicalContext": "CPU", 23 | "RelatedItem": [ 24 | { 25 | "@odata.id": {{ "/redfish/v1/Systems/%s/Processors/CPU"|format(system)|tojson }} 26 | } 27 | ] 28 | }, 29 | {% endfor -%} 30 | { 31 | "@odata.id": {{ "/redfish/v1/Chassis/%s/Thermal#/Temperatures/%d"|format(chassis, systems|length)|tojson }}, 32 | "MemberId": {{ systems|length|string|tojson }}, 33 | "Name": "Chassis Intake Temp", 34 | "SensorNumber": {{ systems|length|string|tojson }}, 35 | "Status": { 36 | "State": "Enabled", 37 | "Health": "OK" 38 | }, 39 | "ReadingCelsius": 25, 40 | "UpperThresholdNonCritical": 30, 41 | "UpperThresholdCritical": 40, 42 | "UpperThresholdFatal": 50, 43 | "LowerThresholdNonCritical": 10, 44 | "LowerThresholdCritical": 5, 45 | "LowerThresholdFatal": 0, 46 | "MinReadingRangeTemp": 0, 47 | "MaxReadingRangeTemp": 60, 48 | "PhysicalContext": "Intake", 49 | "RelatedItem": [ 50 | { 51 | "@odata.id": {{ "/redfish/v1/Chassis/%s"|format(chassis)|tojson }} 52 | }{% if systems %},{% endif %} 53 | {%- for system in systems %} 54 | { 55 | "@odata.id": {{ "/redfish/v1/Systems/%s"|format(system)|tojson }} 56 | }{% if not loop.last %},{% endif %} 57 | {% endfor -%} 58 | ] 59 | } 60 | ], 61 | "Fans": [ 62 | { 63 | "@odata.id": {{ "/redfish/v1/Chassis/%s/Thermal#/Fans/0"|format(chassis)|tojson }}, 64 | "MemberId": "0", 65 | "Name": "BaseBoard System Fan", 66 | "PhysicalContext": "Backplane", 67 | "Status": { 68 | "State": "Enabled", 69 | "Health": "OK" 70 | }, 71 | "Reading": 2100, 72 | "ReadingUnits": "RPM", 73 | "LowerThresholdFatal": 0, 74 | "MinReadingRange": 0, 75 | "MaxReadingRange": 5000, 76 | "Redundancy": [ 77 | { 78 | "@odata.id": {{ "/redfish/v1/Chassis/%s/Thermal#/Redundancy/0"|format(chassis)|tojson }} 79 | } 80 | ], 81 | "RelatedItem": [ 82 | { 83 | "@odata.id": {{ "/redfish/v1/Chassis/%s"|format(chassis)|tojson }} 84 | }{% if systems %},{% endif %} 85 | {%- for system in systems %} 86 | { 87 | "@odata.id": {{ "/redfish/v1/Systems/%s"|format(system)|tojson }} 88 | }{% if not loop.last %},{% endif %} 89 | {% endfor -%} 90 | ] 91 | }, 92 | { 93 | "@odata.id": {{ "/redfish/v1/Chassis/%s/Thermal#/Fans/1"|format(chassis)|tojson }}, 94 | "MemberId": "1", 95 | "Name": "BaseBoard System Fan Backup", 96 | "PhysicalContext": "Backplane", 97 | "Status": { 98 | "State": "Enabled", 99 | "Health": "OK" 100 | }, 101 | "Reading": 2050, 102 | "ReadingUnits": "RPM", 103 | "LowerThresholdFatal": 0, 104 | "MinReadingRange": 0, 105 | "MaxReadingRange": 5000, 106 | "Redundancy": [ 107 | { 108 | "@odata.id": {{ "/redfish/v1/Chassis/%s/Thermal#/Redundancy/0"|format(chassis)|tojson }} 109 | } 110 | ], 111 | "RelatedItem": [ 112 | { 113 | "@odata.id": {{ "/redfish/v1/Chassis/%s"|format(chassis)|tojson }} 114 | }{% if systems %},{% endif %} 115 | {%- for system in systems %} 116 | { 117 | "@odata.id": {{ "/redfish/v1/Systems/%s"|format(system)|tojson }} 118 | }{% if not loop.last %},{% endif %} 119 | {% endfor -%} 120 | ] 121 | } 122 | ], 123 | "@odata.context": "/redfish/v1/$metadata#Thermal.Thermal", 124 | "@odata.id": {{ "/redfish/v1/Chassis/%s/Thermal"|format(chassis)|tojson }}, 125 | "@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 126 | } -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/update_service.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.context": "/redfish/v1/$metadata#UpdateService.UpdateService", 3 | "@odata.id": "/redfish/v1/UpdateService", 4 | "@odata.type": "#UpdateService.v1_11_0.UpdateService", 5 | "Actions": { 6 | "#UpdateService.SimpleUpdate": { 7 | "TransferProtocol@Redfish.AllowableValues": [ 8 | "HTTP", 9 | "HTTPS" 10 | ], 11 | "target": "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate" 12 | } 13 | }, 14 | "Description": "Represents the properties for the Update Service", 15 | "FirmwareInventory": { 16 | "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory" 17 | }, 18 | "HttpPushUri": "/redfish/v1/UpdateService/FirmwareInventory", 19 | "Id": "UpdateService", 20 | "MaxImageSizeBytes": null, 21 | "Name": "Update Service", 22 | "ServiceEnabled": true, 23 | "SoftwareInventory": { 24 | "@odata.id": "/redfish/v1/UpdateService/SoftwareInventory" 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/virtual_media.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#VirtualMedia.v1_4_0.VirtualMedia", 3 | "Id": {{ device|string|tojson }}, 4 | "Name": {{ name|string|tojson }}, 5 | "MediaTypes": [ 6 | {% for media_type in media_types -%} 7 | {{ media_type|string|tojson }}{% if not loop.last %},{% endif %} 8 | {% endfor -%} 9 | ], 10 | "Image": {{ image_url|string|tojson }}, 11 | "ImageName": {{ image_name|string|tojson }}, 12 | "ConnectedVia": "URI", 13 | "Inserted": {{ inserted|tojson }}, 14 | "WriteProtected": {{ write_protected|tojson }}, 15 | "Actions": { 16 | "#VirtualMedia.EjectMedia": { 17 | "target": {{ "/redfish/v1/Systems/%s/VirtualMedia/%s/Actions/VirtualMedia.EjectMedia"|format(identity, device)|string|tojson }} 18 | }, 19 | "#VirtualMedia.InsertMedia": { 20 | "target": {{ "/redfish/v1/Systems/%s/VirtualMedia/%s/Actions/VirtualMedia.InsertMedia"|format(identity, device)|string|tojson }} 21 | }, 22 | "Oem": {} 23 | }, 24 | "UserName": {{ username|string|tojson }}, 25 | "Password": "{{ '******' if password else '' }}", 26 | "Certificates": { 27 | "@odata.id": {{ "/redfish/v1/Systems/%s/VirtualMedia/%s/Certificates"|format(identity, device)|tojson }} 28 | }, 29 | "VerifyCertificate": {{ verify_certificate|tojson }}, 30 | "@odata.context": "/redfish/v1/$metadata#VirtualMedia.VirtualMedia", 31 | "@odata.id": {{ "/redfish/v1/Systems/%s/VirtualMedia/%s"|format(identity, device)|string|tojson }}, 32 | "@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 33 | } 34 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/virtual_media_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#VirtualMediaCollection.VirtualMediaCollection", 3 | "Name": "Virtual Media Services", 4 | "Description": "Redfish-BMC Virtual Media Service Settings", 5 | "Members@odata.count": {{ devices|length }}, 6 | "Members": [ 7 | {% for device in devices %} 8 | { 9 | "@odata.id": {{ "/redfish/v1/Systems/%s/VirtualMedia/%s"|format(uuid, device)|string|tojson }} 10 | }{% if not loop.last %},{% endif %} 11 | {% endfor %} 12 | ], 13 | "@odata.context": "/redfish/v1/$metadata#VirtualMediaCollection.VirtualMediaCollection", 14 | "@odata.id": {{ "/redfish/v1/Systems/%s/VirtualMedia"|format(uuid)|string|tojson }}, 15 | "@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 16 | } 17 | -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/volume.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#Volume.v1_0_3.Volume", 3 | "Id": {{ volume['Id']|string|tojson }}, 4 | "Name": {{ volume['Name']|string|tojson }}, 5 | "Status": { 6 | "@odata.type": "#Resource.Status", 7 | "State": "Enabled", 8 | "Health": "OK" 9 | }, 10 | "Encrypted": false, 11 | "VolumeType": {{ volume['VolumeType']|string|tojson }}, 12 | "CapacityBytes": {{ volume['CapacityBytes'] }}, 13 | "@odata.context": "/redfish/v1/$metadata#Volume.Volume", 14 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage/%s/Volumes/%s"|format(identity, storage_id, volume['Id'])|tojson }}, 15 | "@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 16 | } -------------------------------------------------------------------------------- /sushy_tools/emulator/templates/volume_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "@odata.type": "#VolumeCollection.VolumeCollection", 3 | "Name": "Storage Volume Collection", 4 | "Members@odata.count": {{ volume_col|length }}, 5 | "Members": [ 6 | {% for volume in volume_col %} 7 | { 8 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage/%s/Volumes/%s"|format(identity, storage_id, volume)|tojson }} 9 | }{% if not loop.last %},{% endif %} 10 | {% endfor %} 11 | ], 12 | "@odata.context": "/redfish/v1/$metadata#VolumeCollection.VolumeCollection", 13 | "@odata.id": {{ "/redfish/v1/Systems/%s/Storage/%s/Volumes"|format(identity, storage_id)|tojson }}, 14 | "@Redfish.Copyright": "Copyright 2014-2017 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." 15 | } -------------------------------------------------------------------------------- /sushy_tools/error.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | 17 | class FishyError(Exception): 18 | """Create generic sushy-tools exception object""" 19 | 20 | def __init__(self, msg='Unknown error', code=500): 21 | super().__init__(msg) 22 | self.code = code 23 | 24 | 25 | class AliasAccessError(FishyError): 26 | """Node access attempted via an alias, not UUID""" 27 | 28 | 29 | class NotSupportedError(FishyError): 30 | """Feature not supported by resource driver""" 31 | 32 | def __init__(self, msg='Unsupported', code=501): 33 | super().__init__(msg, code) 34 | 35 | 36 | class NotFound(FishyError): 37 | """Entity not found.""" 38 | 39 | def __init__(self, msg='Not found', code=404): 40 | super().__init__(msg, code) 41 | 42 | 43 | class BadRequest(FishyError): 44 | """Malformed request.""" 45 | 46 | def __init__(self, msg, code=400): 47 | super().__init__(msg, code) 48 | 49 | 50 | class FeatureNotAvailable(NotFound): 51 | """Feature is not available.""" 52 | 53 | def __init__(self, feature, code=404): 54 | super().__init__(f"Feature {feature} not available", code=code) 55 | 56 | 57 | class Conflict(FishyError): 58 | """Conflict with current state of the resource.""" 59 | 60 | def __init__(self, msg, code=409): 61 | super().__init__(msg, code) 62 | 63 | 64 | class ConfigInvalid(FishyError): 65 | """Config is invalid.""" 66 | 67 | def __init__(self, msg, code=500): 68 | errmsg = f"Invalid configuration file. {msg}" 69 | super().__init__(errmsg, code) 70 | 71 | 72 | class Unauthorized(FishyError): 73 | """Unauthorized for resource""" 74 | 75 | def __init__(self, msg, code=401): 76 | self.headers = {'WWW-Authenticate': 'Basic realm="Baremetal API"'} 77 | super().__init__(msg, code) 78 | -------------------------------------------------------------------------------- /sushy_tools/static/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/static/__init__.py -------------------------------------------------------------------------------- /sushy_tools/static/main.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import argparse 17 | import os 18 | import ssl 19 | import sys 20 | 21 | try: 22 | from http import server as http_server 23 | 24 | except ImportError: 25 | import BaseHTTPServer as http_server # Py2 26 | 27 | REDFISH_MOCKUP_FILES = None 28 | 29 | 30 | class RequestHandler(http_server.BaseHTTPRequestHandler): 31 | 32 | REDFISH_SUBURI = '/redfish/v1' 33 | 34 | def _log_request(self, method): 35 | print(self.headers) 36 | content_length = int(self.headers.get('content-length', 0)) 37 | if content_length > 0: 38 | print('Data: %s\n' % self.rfile.read(content_length)) 39 | 40 | def do_GET(self): 41 | self._log_request('GET') 42 | path = self.path.rstrip('/') 43 | if not path.startswith(self.REDFISH_SUBURI): 44 | self.send_error(404) 45 | return 46 | 47 | resource_path = path.replace(self.REDFISH_SUBURI, '').lstrip('/') 48 | fpath = os.path.join(REDFISH_MOCKUP_FILES, resource_path, 'index.json') 49 | if not os.path.exists(fpath): 50 | self.send_error(404, 'Sub-URI %s not found' % resource_path) 51 | return 52 | 53 | self.send_response(200) 54 | self.send_header('Content-type', 'application/json') 55 | self.end_headers() 56 | 57 | with open(fpath, 'r') as f: 58 | self.wfile.write(f.read().encode('utf-8')) 59 | 60 | def do_POST(self): 61 | self._log_request('POST') 62 | self.send_response(204) 63 | self.end_headers() 64 | 65 | def do_PATCH(self): 66 | self._log_request('PATCH') 67 | self.send_response(204) 68 | self.end_headers() 69 | 70 | 71 | def parse_args(): 72 | parser = argparse.ArgumentParser('sushy-static') 73 | parser.add_argument('-i', '--interface', 74 | type=str, 75 | default='', 76 | help='Local interface to listen at') 77 | parser.add_argument('-p', '--port', 78 | type=int, 79 | default=8000, 80 | help='The port to bind the server to') 81 | parser.add_argument('-m', '--mockup-files', 82 | type=str, 83 | required=True, 84 | help=('The path to the Redfish Mockup files in ' 85 | 'the filesystem')) 86 | parser.add_argument('-c', '--ssl-certificate', 87 | type=str, 88 | help='SSL certificate to use for HTTPS') 89 | parser.add_argument('-k', '--ssl-key', 90 | type=str, 91 | help='SSL key to use for HTTPS') 92 | return parser.parse_args() 93 | 94 | 95 | def main(): 96 | global REDFISH_MOCKUP_FILES 97 | 98 | args = parse_args() 99 | if not os.path.exists(args.mockup_files): 100 | print('Mockup files %s not found' % args.mockup_files) 101 | return 1 102 | 103 | REDFISH_MOCKUP_FILES = os.path.realpath(args.mockup_files) 104 | httpd = http_server.HTTPServer((args.interface, args.port), 105 | RequestHandler) 106 | 107 | if args.ssl_certificate and args.ssl_key: 108 | context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) 109 | context.load_cert_chain(args.ssl_certificate, args.ssl_key) 110 | httpd.socket = context.wrap_socket(httpd.socket, server_side=True) 111 | 112 | httpd.serve_forever() 113 | 114 | return 0 115 | 116 | 117 | if __name__ == '__main__': 118 | sys.exit(main()) 119 | -------------------------------------------------------------------------------- /sushy_tools/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/tests/__init__.py -------------------------------------------------------------------------------- /sushy_tools/tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/tests/unit/__init__.py -------------------------------------------------------------------------------- /sushy_tools/tests/unit/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2010-2011 OpenStack Foundation 4 | # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. You may obtain 8 | # a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | # License for the specific language governing permissions and limitations 16 | # under the License. 17 | 18 | from oslotest import base 19 | 20 | from sushy_tools.emulator import main 21 | 22 | 23 | class TestCase(base.BaseTestCase): 24 | """Test case base class for all unit tests""" 25 | 26 | def setUp(self): 27 | super().setUp() 28 | main.app._cache = {} 29 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/tests/unit/emulator/__init__.py -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/controllers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/tests/unit/emulator/controllers/__init__.py -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/controllers/test_certificate_service.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from sushy_tools.emulator.resources import vmedia 14 | from sushy_tools import error 15 | from sushy_tools.tests.unit.emulator import test_main 16 | 17 | 18 | @test_main.patch_resource('vmedia') 19 | @test_main.patch_resource('managers') 20 | class CertificateServiceTestCase(test_main.EmulatorTestCase): 21 | 22 | def test_root(self, managers_mock, vmedia_mock): 23 | response = self.app.get('redfish/v1/CertificateService') 24 | 25 | self.assertEqual(200, response.status_code) 26 | self.assertIn('#CertificateService.ReplaceCertificate', 27 | response.json['Actions']) 28 | 29 | def test_replace_ok(self, managers_mock, vmedia_mock): 30 | response = self.app.post( 31 | 'redfish/v1/CertificateService/Actions/' 32 | 'CertificateService.ReplaceCertificate', 33 | json={'CertificateString': 'abcd', 34 | 'CertificateType': 'PEM', 35 | 'CertificateUri': ('https://host/redfish/v1/Managers/1234' 36 | '/VirtualMedia/CD/Certificates/1')}) 37 | 38 | self.assertEqual(204, response.status_code) 39 | managers_mock.return_value.get_manager.assert_called_once_with('1234') 40 | vmedia_mock.return_value.replace_certificate.assert_called_once_with( 41 | '1234', 'CD', '1', 'abcd', 'PEM') 42 | 43 | def test_replace_manager_not_found(self, managers_mock, vmedia_mock): 44 | managers_mock.return_value.get_manager.side_effect = error.NotFound 45 | 46 | response = self.app.post( 47 | 'redfish/v1/CertificateService/Actions/' 48 | 'CertificateService.ReplaceCertificate', 49 | json={'CertificateString': 'abcd', 50 | 'CertificateType': 'PEM', 51 | 'CertificateUri': ('https://host/redfish/v1/Managers/1234' 52 | '/VirtualMedia/CD/Certificates/1')}) 53 | 54 | self.assertEqual(404, response.status_code) 55 | managers_mock.return_value.get_manager.assert_called_once_with('1234') 56 | vmedia_mock.return_value.replace_certificate.assert_not_called() 57 | 58 | def test_replace_wrong_uri(self, managers_mock, vmedia_mock): 59 | response = self.app.post( 60 | 'redfish/v1/CertificateService/Actions/' 61 | 'CertificateService.ReplaceCertificate', 62 | json={'CertificateString': 'abcd', 63 | 'CertificateType': 'PEM', 64 | 'CertificateUri': ('https://host/redfish/v1/Managers/1234' 65 | '/NetworkProtocol/HTTPS/Certificates/1')}) 66 | 67 | self.assertEqual(404, response.status_code) 68 | managers_mock.return_value.get_manager.assert_not_called() 69 | vmedia_mock.return_value.replace_certificate.assert_not_called() 70 | 71 | def test_replace_missing_string(self, managers_mock, vmedia_mock): 72 | response = self.app.post( 73 | 'redfish/v1/CertificateService/Actions/' 74 | 'CertificateService.ReplaceCertificate', 75 | json={'CertificateType': 'PEM', 76 | 'CertificateUri': ('https://host/redfish/v1/Managers/1234' 77 | '/VirtualMedia/CD/Certificates/1')}) 78 | 79 | self.assertEqual(400, response.status_code) 80 | managers_mock.return_value.get_manager.assert_not_called() 81 | vmedia_mock.return_value.replace_certificate.assert_not_called() 82 | 83 | def test_replace_wrong_type(self, managers_mock, vmedia_mock): 84 | response = self.app.post( 85 | 'redfish/v1/CertificateService/Actions/' 86 | 'CertificateService.ReplaceCertificate', 87 | json={'CertificateString': 'abcd', 88 | 'CertificateType': 'non-PEM', 89 | 'CertificateUri': ('https://host/redfish/v1/Managers/1234' 90 | '/VirtualMedia/CD/Certificates/1')}) 91 | 92 | self.assertEqual(400, response.status_code) 93 | managers_mock.return_value.get_manager.assert_not_called() 94 | vmedia_mock.return_value.replace_certificate.assert_not_called() 95 | 96 | def test_replace_missing_uri(self, managers_mock, vmedia_mock): 97 | response = self.app.post( 98 | 'redfish/v1/CertificateService/Actions/' 99 | 'CertificateService.ReplaceCertificate', 100 | json={'CertificateString': 'abcd', 101 | 'CertificateType': 'PEM'}) 102 | 103 | self.assertEqual(400, response.status_code) 104 | managers_mock.return_value.get_manager.assert_not_called() 105 | vmedia_mock.return_value.replace_certificate.assert_not_called() 106 | 107 | def test_locations(self, managers_mock, vmedia_mock): 108 | managers_mock.return_value.managers = ["1", "2"] 109 | vmedia_mock.return_value.devices = ["CD", "DVD"] 110 | vmedia_mock.return_value.list_certificates.side_effect = [ 111 | error.NotFound(), 112 | [vmedia.Certificate("cert1", "abcd", "PEM")], 113 | [vmedia.Certificate("cert2", "abcd", "PEM")], 114 | error.NotFound(), 115 | ] 116 | response = self.app.get( 117 | 'redfish/v1/CertificateService/CertificateLocations') 118 | 119 | self.assertEqual(200, response.status_code) 120 | self.assertEqual( 121 | ['/redfish/v1/Managers/1/VirtualMedia/DVD/Certificates/cert1', 122 | '/redfish/v1/Managers/2/VirtualMedia/CD/Certificates/cert2'], 123 | [item['@odata.id'] 124 | for item in response.json['Links']['Certificates']]) 125 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain-q35.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | 10 | 11 | 12 | 13 | /usr/bin/qemu-system-x86_64 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain-q35_fw_auto_uefi.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | 10 | 11 | 12 | 13 | 14 | 15 | /usr/bin/qemu-system-x86_64 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain-q35_fw_auto_uefi_secure.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | 10 | 11 | 12 | 13 | 14 | 15 | /usr/bin/qemu-system-x86_64 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain-q35_uefi.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | /usr/share/OVMF/OVMF_CODE.fd 10 | /var/lib/libvirt/nvram/QEmu-fedora-i686_VARS.fd 11 | 12 | 13 | 14 | /usr/bin/qemu-system-x86_64 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain-q35_uefi_secure.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | /usr/share/OVMF/OVMF_CODE.secboot.fd 10 | /var/lib/libvirt/nvram/QEmu-fedora-i686_VARS.fd 11 | 12 | 13 | 14 | /usr/bin/qemu-system-x86_64 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain-sata.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | 10 | 11 | 12 | 13 | /usr/bin/qemu-system-x86_64 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain-scsi.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | 10 | 11 | 12 | 13 | /usr/bin/qemu-system-x86_64 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | 10 | 11 | 12 | 13 | /usr/bin/qemu-system-x86_64 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_bios.xml: -------------------------------------------------------------------------------- 1 | 3 | QEmu-fedora-i686 4 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 5 | 219200 6 | 219200 7 | 2 8 | 9 | hvm 10 | 11 | 12 | 13 | 14 | /usr/bin/qemu-system-x86_64 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_boot_disk.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_boot_network.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_boot_os.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_fw_auto.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | 10 | 11 | 12 | /usr/bin/qemu-system-x86_64 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | 10 | 11 | 12 | 13 | /usr/bin/qemu-system-x86_64 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_nics.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | 10 | 11 | 12 | 13 | /usr/bin/qemu-system-x86_64 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 |
33 | 34 | 35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_processors.xml: -------------------------------------------------------------------------------- 1 | 2 | test-node 3 | da64d3a4-0b06-428b-9afb-73b4a4101bb3 4 | 1048576 5 | 1048576 6 | 2 7 | 8 | /machine 9 | 10 | 11 | hvm 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | core2duo 20 | Intel 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | destroy 30 | restart 31 | destroy 32 | 33 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_processors_notopology.xml: -------------------------------------------------------------------------------- 1 | 2 | test-node 3 | 5729db1e-3162-40e4-9fc4-817e4c0c121d 4 | 2097152 5 | 2097152 6 | 1 7 | 8 | hvm 9 | /usr/share/OVMF/OVMF_CODE.fd 10 | /var/lib/libvirt/qemu/nvram/test-node_VARS.fd 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | destroy 25 | restart 26 | destroy 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_simple_storage.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | 10 | 11 | 12 | 13 | /usr/bin/qemu-system-x86_64 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_to_boot_pxe.xml: -------------------------------------------------------------------------------- 1 | 2 | QEmu-fedora-i686 3 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 4 | 219200 5 | 219200 6 | 2 7 | 8 | hvm 9 | 10 | 11 | 12 | 13 | /usr/bin/qemu-system-x86_64 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/domain_versions.xml: -------------------------------------------------------------------------------- 1 | 3 | QEmu-fedora-i686 4 | c7a5fdbd-cdaf-9455-926a-d65c16db1809 5 | 219200 6 | 219200 7 | 2 8 | 9 | hvm 10 | 11 | 12 | 13 | 14 | /usr/bin/qemu-system-x86_64 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/pool.xml: -------------------------------------------------------------------------------- 1 | 2 | default 3 | 267e1242-d53f-46dd-adb3-9f3992c55f6f 4 | 166318571520 5 | 13143412736 6 | 153175158784 7 | 8 | 9 | 10 | /var/lib/libvirt/images 11 | 12 | 0711 13 | 0 14 | 0 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/tests/unit/emulator/resources/__init__.py -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/resources/systems/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/sushy-tools/bb880ccf42ae93f8c06025750e223a0e72604e05/sushy_tools/tests/unit/emulator/resources/systems/__init__.py -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/resources/systems/test_fakedriver.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import time 14 | from unittest import mock 15 | 16 | from oslotest import base 17 | 18 | from sushy_tools.emulator.resources.systems import fakedriver 19 | from sushy_tools import error 20 | 21 | 22 | UUID = fakedriver.DEFAULT_UUID 23 | 24 | 25 | class FakeDriverTestCase(base.BaseTestCase): 26 | 27 | def setUp(self): 28 | super().setUp() 29 | test_driver_class = fakedriver.FakeDriver.initialize( 30 | {}, mock.MagicMock()) 31 | self.cache = {} 32 | with mock.patch('sushy_tools.emulator.memoize.PersistentDict', 33 | return_value=self.cache, autospec=True): 34 | self.test_driver = test_driver_class() 35 | 36 | def test_systems(self): 37 | self.assertEqual([UUID], self.test_driver.systems) 38 | self.assertEqual('fake', self.test_driver.name(UUID)) 39 | self.assertEqual(UUID, self.test_driver.uuid('fake')) 40 | self.assertEqual(UUID, self.test_driver.uuid(UUID)) 41 | self.assertEqual('fake', self.test_driver.name('fake')) 42 | self.assertRaises(error.NotFound, self.test_driver.uuid, 'foo') 43 | self.assertRaises(error.NotFound, self.test_driver.name, 'foo') 44 | 45 | @mock.patch('random.randint', autospec=True, return_value=0) 46 | def test_power_state(self, mock_rand): 47 | self.assertEqual('Off', self.test_driver.get_power_state(UUID)) 48 | self.test_driver.set_power_state(UUID, 'On') 49 | self.assertEqual('On', self.test_driver.get_power_state(UUID)) 50 | self.test_driver.set_power_state(UUID, 'ForceOff') 51 | self.assertEqual('Off', self.test_driver.get_power_state(UUID)) 52 | 53 | @mock.patch('random.randint', autospec=True, return_value=1000) 54 | def test_power_state_delay(self, mock_rand): 55 | self.assertEqual('Off', self.test_driver.get_power_state(UUID)) 56 | self.test_driver.set_power_state(UUID, 'On') 57 | self.assertEqual('Off', self.test_driver.get_power_state(UUID)) 58 | 59 | new_time = time.time() + 2000 60 | with mock.patch.object(time, 'time', autospec=True, 61 | return_value=new_time): 62 | self.assertEqual('On', self.test_driver.get_power_state(UUID)) 63 | 64 | @mock.patch('random.randint', autospec=True, return_value=1000) 65 | def test_reboot_delay(self, mock_rand): 66 | self.cache[UUID]['power_state'] = 'On' 67 | 68 | self.assertEqual('On', self.test_driver.get_power_state(UUID)) 69 | self.test_driver.set_power_state(UUID, 'ForceRestart') 70 | self.assertEqual('Off', self.test_driver.get_power_state(UUID)) 71 | 72 | new_time = time.time() + 2000 73 | with mock.patch.object(time, 'time', autospec=True, 74 | return_value=new_time): 75 | self.assertEqual('On', self.test_driver.get_power_state(UUID)) 76 | 77 | def test_boot_mode(self): 78 | self.assertEqual('UEFI', self.test_driver.get_boot_mode(UUID)) 79 | self.test_driver.set_boot_mode(UUID, 'legacy') 80 | self.assertEqual('legacy', self.test_driver.get_boot_mode(UUID)) 81 | 82 | def test_boot_device(self): 83 | self.assertEqual('Hdd', self.test_driver.get_boot_device(UUID)) 84 | self.test_driver.set_boot_device(UUID, 'Cd') 85 | self.assertEqual('Cd', self.test_driver.get_boot_device(UUID)) 86 | 87 | def test_boot_image(self): 88 | self.assertEqual((None, False, False), 89 | self.test_driver.get_boot_image(UUID, 'Cd')) 90 | self.test_driver.set_boot_image(UUID, 'Cd', 'http://example') 91 | self.assertEqual(('http://example', True, True), 92 | self.test_driver.get_boot_image(UUID, 'Cd')) 93 | self.assertEqual((None, False, False), 94 | self.test_driver.get_boot_image(UUID, 'Hdd')) 95 | 96 | def test_secure_boot(self): 97 | self.assertFalse(self.test_driver.get_secure_boot(UUID)) 98 | self.test_driver.set_secure_boot(UUID, True) 99 | self.assertTrue(self.test_driver.get_secure_boot(UUID)) 100 | 101 | def test_interface(self): 102 | self.assertEqual([{'id': '00:5c:52:31:3a:9c', 103 | 'mac': '00:5c:52:31:3a:9c'}], 104 | self.test_driver.get_nics(UUID)) 105 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/resources/test_chassis.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | from unittest import mock 16 | import uuid 17 | 18 | from oslotest import base 19 | 20 | from sushy_tools.emulator.resources.chassis import StaticDriver 21 | from sushy_tools import error 22 | 23 | 24 | class StaticDriverTestCase(base.BaseTestCase): 25 | 26 | def setUp(self): 27 | self.chassis = [ 28 | { 29 | "Id": "Chassis", 30 | "Name": "Tinfoil Chassis", 31 | "UUID": "48295861-2522-3561-6729-621118518810", 32 | "Contains": ['48295861-2522-3561-6729-621118518810'], 33 | "ContainedBy": 'ZZZ-YYY-XXX' 34 | } 35 | ] 36 | 37 | self.identity = self.chassis[0]['Id'] 38 | self.uuid = self.chassis[0]['UUID'] 39 | self.name = self.chassis[0]['Name'] 40 | 41 | self.sys_name = 'server01' 42 | self.sys_uuid = self.uuid.replace('8', '1') 43 | 44 | self.mgr_name = 'manager01' 45 | self.mgr_uuid = self.uuid.replace('8', '2') 46 | 47 | self.test_driver = StaticDriver( 48 | {'SUSHY_EMULATOR_CHASSIS': self.chassis}, mock.MagicMock()) 49 | 50 | systems_mock = mock.MagicMock() 51 | systems_mock.name.return_value = self.sys_name 52 | systems_mock.uuid.return_value = self.sys_uuid 53 | systems_mock.systems = [self.sys_name] 54 | 55 | self.test_driver.systems = systems_mock 56 | 57 | managers_mock = mock.MagicMock() 58 | managers_mock.name.return_value = self.mgr_name 59 | managers_mock.uuid.return_value = self.mgr_uuid 60 | managers_mock.managers = [self.mgr_name] 61 | 62 | self.test_driver.managers = managers_mock 63 | 64 | super(StaticDriverTestCase, self).setUp() 65 | 66 | def test__get_chassis_by_id(self): 67 | self.assertRaises( 68 | error.AliasAccessError, self.test_driver._get_chassis, 69 | self.identity) 70 | 71 | def test__get_chassis_by_name(self): 72 | self.assertRaises( 73 | error.AliasAccessError, self.test_driver._get_chassis, self.name) 74 | 75 | def test__get_chassis_by_uuid(self): 76 | chassis_uuid = uuid.UUID(self.uuid) 77 | chassis = self.test_driver._get_chassis(str(chassis_uuid)) 78 | self.assertEqual( 79 | self.chassis[0], chassis) 80 | 81 | def test_uuid_ok(self): 82 | self.assertEqual(self.uuid, self.test_driver.uuid(self.uuid)) 83 | 84 | def test_uuid_fail(self): 85 | self.assertRaises(error.FishyError, self.test_driver.uuid, 'xxx') 86 | 87 | def test_name_ok(self): 88 | self.assertRaises(error.AliasAccessError, 89 | self.test_driver.name, self.name) 90 | 91 | def test_name_fail(self): 92 | self.assertRaises(error.FishyError, self.test_driver.name, 'xxx') 93 | 94 | def test_chassis(self): 95 | chassis = self.test_driver.chassis 96 | self.assertEqual([self.uuid], chassis) 97 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/resources/test_drives.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from unittest import mock 17 | 18 | from oslotest import base 19 | 20 | from sushy_tools.emulator.resources.drives import StaticDriver 21 | 22 | 23 | class StaticDriverTestCase(base.BaseTestCase): 24 | SYSTEM_UUID = "da69abcc-dae0-4913-9a7b-d344043097c0" 25 | STORAGE_ID = "1" 26 | DRIVE_COL = [ 27 | { 28 | "Id": "32ADF365C6C1B7BD", 29 | "Name": "Drive Sample", 30 | "CapacityBytes": 899527000000, 31 | "Protocol": "SAS" 32 | }, 33 | { 34 | "Id": "58CFF987G8J2V9KL", 35 | "Name": "Drive2", 36 | "CapacityBytes": 12345670000, 37 | "Protocol": "SATA" 38 | } 39 | ] 40 | 41 | CONFIG = { 42 | 'SUSHY_EMULATOR_DRIVES': { 43 | (SYSTEM_UUID, STORAGE_ID): DRIVE_COL 44 | } 45 | } 46 | 47 | def test_get_drives(self): 48 | test_driver = StaticDriver(self.CONFIG, mock.MagicMock()) 49 | drv_col = test_driver.get_drives(self.SYSTEM_UUID, self.STORAGE_ID) 50 | self.assertEqual(self.DRIVE_COL, drv_col) 51 | 52 | def test_get_all_drives(self): 53 | test_driver = StaticDriver(self.CONFIG, mock.MagicMock()) 54 | drives = test_driver.get_all_drives() 55 | self.assertEqual({('da69abcc-dae0-4913-9a7b-d344043097c0', '1', 56 | '32ADF365C6C1B7BD'), 57 | ('da69abcc-dae0-4913-9a7b-d344043097c0', '1', 58 | '58CFF987G8J2V9KL')}, set(drives)) 59 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/resources/test_indicators.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | from unittest import mock 16 | 17 | from oslotest import base 18 | 19 | from sushy_tools.emulator.resources.indicators import StaticDriver 20 | from sushy_tools import error 21 | 22 | 23 | class StaticDriverTestCase(base.BaseTestCase): 24 | 25 | UUID = "58893887-8974-2487-2389-841168418919" 26 | STATE = "Off" 27 | 28 | CONFIG = { 29 | 'SUSHY_EMULATOR_INDICATOR_LEDS': { 30 | UUID: STATE 31 | } 32 | } 33 | 34 | def setUp(self): 35 | super().setUp() 36 | with mock.patch('sushy_tools.emulator.memoize.PersistentDict', 37 | return_value={}, autospec=True): 38 | self.test_driver = StaticDriver(self.CONFIG, mock.MagicMock()) 39 | 40 | def test_indicators(self): 41 | indicators = self.test_driver.indicators 42 | self.assertEqual([self.UUID], indicators) 43 | 44 | def test_get_indicator_state(self): 45 | state = self.test_driver.get_indicator_state(self.UUID) 46 | self.assertEqual('Off', state) 47 | 48 | def test_set_indicator_state_ok(self): 49 | self.test_driver.set_indicator_state(self.UUID, 'Lit') 50 | state = self.test_driver.get_indicator_state(self.UUID) 51 | self.assertEqual('Lit', state) 52 | 53 | def test_set_indicator_state_fail(self): 54 | self.assertRaises( 55 | error.FishyError, 56 | self.test_driver.set_indicator_state, 57 | self.UUID, 'Blah') 58 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/resources/test_managers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from unittest import mock 17 | 18 | from oslotest import base 19 | 20 | from sushy_tools.emulator.resources import managers 21 | from sushy_tools import error 22 | 23 | 24 | class FakeDriverTestCase(base.BaseTestCase): 25 | 26 | def setUp(self): 27 | super(FakeDriverTestCase, self).setUp() 28 | self.identity = 'xxx' 29 | self.systems = mock.Mock(systems=[self.identity]) 30 | self.systems.uuid.return_value = 'xxx' 31 | self.systems.name.return_value = 'name' 32 | self.manager = {'UUID': self.identity, 33 | 'Id': self.identity, 34 | 'Name': 'name-Manager'} 35 | self.chassis = mock.Mock(chassis=[]) 36 | self.test_driver = managers.FakeDriver({}, mock.Mock(), 37 | self.systems, self.chassis) 38 | 39 | def test_get_manager_not_found(self): 40 | self.systems.uuid.side_effect = error.FishyError('boom') 41 | self.assertRaises( 42 | error.FishyError, self.test_driver.get_manager, 'foo') 43 | 44 | def test_get_manager_by_uuid(self): 45 | manager = self.test_driver.get_manager('xxx') 46 | self.assertEqual(self.manager, manager) 47 | 48 | def test_managers(self): 49 | result = self.test_driver.managers 50 | self.assertEqual([self.identity], result) 51 | 52 | def test_managed_systems(self): 53 | self.assertEqual( 54 | ['xxx'], self.test_driver.get_managed_systems(self.manager)) 55 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/resources/test_storage.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from unittest import mock 17 | 18 | from oslotest import base 19 | 20 | from sushy_tools.emulator.resources.storage import StaticDriver 21 | 22 | 23 | class StaticDriverTestCase(base.BaseTestCase): 24 | UUID = "da69abcc-dae0-4913-9a7b-d344043097c0" 25 | STORAGE_COL = [ 26 | { 27 | "Id": "1", 28 | "Name": "Local Storage Controller", 29 | "StorageControllers": [ 30 | { 31 | "MemberId": "0", 32 | "Name": "Contoso Integrated RAID", 33 | "SpeedGbps": 12 34 | } 35 | ] 36 | } 37 | ] 38 | 39 | CONFIG = { 40 | 'SUSHY_EMULATOR_STORAGE': { 41 | UUID: STORAGE_COL 42 | } 43 | } 44 | 45 | def test_get_storage_col(self): 46 | test_driver = StaticDriver(self.CONFIG, mock.MagicMock()) 47 | stg_col = test_driver.get_storage_col(self.UUID) 48 | self.assertEqual(self.STORAGE_COL, stg_col) 49 | 50 | def test_get_all_storage(self): 51 | test_driver = StaticDriver(self.CONFIG, mock.MagicMock()) 52 | stg = test_driver.get_all_storage() 53 | self.assertEqual([(self.UUID, '1')], stg) 54 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/resources/test_update_service.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from sushy_tools.tests.unit.emulator import test_main 17 | 18 | 19 | class UpdateServiceTestCase(test_main.EmulatorTestCase): 20 | 21 | def test_update_service_get(self): 22 | actions = { 23 | "#UpdateService.SimpleUpdate": { 24 | "TransferProtocol@Redfish.AllowableValues": [ 25 | "HTTP", 26 | "HTTPS" 27 | ], 28 | "target": "/redfish/v1/UpdateService/Actions/" 29 | "UpdateService.SimpleUpdate" 30 | } 31 | } 32 | 33 | firmwareinventory = { 34 | "@odata.id": "/redfish/v1/UpdateService/FirmwareInventory" 35 | } 36 | 37 | softwareinventory = { 38 | "@odata.id": "/redfish/v1/UpdateService/SoftwareInventory" 39 | } 40 | 41 | resp = self.app.get('/redfish/v1/UpdateService') 42 | self.assertEqual(200, resp.status_code) 43 | 44 | self.assertEqual('/redfish/v1/$metadata#UpdateService.UpdateService', 45 | resp.json['@odata.context']) 46 | self.assertEqual('/redfish/v1/UpdateService', resp.json['@odata.id']) 47 | self.assertEqual('#UpdateService.v1_11_0.UpdateService', 48 | resp.json['@odata.type']) 49 | self.assertEqual(actions, resp.json['Actions']) 50 | self.assertEqual('Represents the properties for the Update Service', 51 | resp.json['Description']) 52 | self.assertEqual(firmwareinventory, resp.json['FirmwareInventory']) 53 | self.assertEqual('/redfish/v1/UpdateService/FirmwareInventory', 54 | resp.json['HttpPushUri']) 55 | self.assertEqual('UpdateService', resp.json['Id']) 56 | self.assertIsNone(resp.json['MaxImageSizeBytes']) 57 | self.assertEqual('Update Service', resp.json['Name']) 58 | self.assertTrue(resp.json['ServiceEnabled']) 59 | self.assertEqual(softwareinventory, resp.json['SoftwareInventory']) 60 | 61 | @test_main.patch_resource('indicators') 62 | @test_main.patch_resource('chassis') 63 | @test_main.patch_resource('managers') 64 | @test_main.patch_resource('systems') 65 | def test_update_service_simpleupdate(self, systems_mock, managers_mock, 66 | chassis_mock, indicators_mock): 67 | systems_mock = systems_mock.return_value 68 | systems_mock.uuid.return_value = 'zzzz-yyyy-xxxx' 69 | systems_mock.get_power_state.return_value = 'On' 70 | systems_mock.get_total_memory.return_value = 1 71 | systems_mock.get_total_cpus.return_value = 2 72 | systems_mock.get_boot_device.return_value = 'Cd' 73 | systems_mock.get_boot_mode.return_value = 'Legacy' 74 | systems_mock.get_versions().get.return_value = '1.0.0' 75 | get_versions = systems_mock.get_versions 76 | managers_mock.return_value.get_managers_for_system.return_value = [ 77 | 'aaaa-bbbb-cccc'] 78 | chassis_mock.return_value.chassis = ['chassis0'] 79 | indicators_mock.return_value.get_indicator_state.return_value = 'Off' 80 | 81 | resp = self.app.get('/redfish/v1/Systems/zzzz-yyyy-xxxx') 82 | self.assertEqual(200, resp.status_code) 83 | self.assertEqual('zzzz-yyyy-xxxx', resp.json['Id']) 84 | self.assertEqual('1.0.0', resp.json['BiosVersion']) 85 | get_versions.assert_called() 86 | 87 | args = { 88 | 'ImageURI': 'http://10.6.48.48:8080/ilo5278.bin', 89 | 'TransferProtocol': 'HTTP', 90 | 'Targets': ['/redfish/v1/Systems/' 91 | 'zzzz-yyyy-xxxx'] 92 | } 93 | set_versions = systems_mock.set_versions 94 | response = self.app.post('/redfish/v1/UpdateService/Actions/' 95 | 'UpdateService.SimpleUpdate', json=args) 96 | self.assertEqual(204, response.status_code) 97 | set_versions.assert_called_once_with('zzzz-yyyy-xxxx', 98 | {'BiosVersion': '1.1.0'}) 99 | 100 | def test_update_service_invalid_params(self): 101 | args = { 102 | 'NonexistentURI': 'asdf://nowhere.com', 103 | } 104 | 105 | response = self.app.post('/redfish/v1/UpdateService/Actions/' 106 | 'UpdateService.SimpleUpdate', json=args) 107 | self.assertEqual('Base.1.0.GeneralError', 108 | response.json['error']['code']) 109 | self.assertEqual('Missing ImageURI and/or Targets.', 110 | response.json['error']['message']) 111 | 112 | @test_main.patch_resource('systems') 113 | def test_update_service_simpleupdate_manager(self, systems_mock): 114 | systems_mock = systems_mock.return_value 115 | systems_mock.uuid.return_value = 'zzzz-yyyy-xxxx' 116 | args = { 117 | 'ImageURI': 'http://10.6.48.48:8080/ilo5278.bin', 118 | 'TransferProtocol': 'HTTP', 119 | 'Targets': ['/redfish/v1/Managers/' 120 | 'zzzz-yyyy-xxxx'] 121 | } 122 | set_versions = systems_mock.set_versions 123 | response = self.app.post('/redfish/v1/UpdateService/Actions/' 124 | 'UpdateService.SimpleUpdate', json=args) 125 | self.assertEqual(400, response.status_code) 126 | self.assertEqual('Manager is not currently a supported Target.', 127 | response.json['error']['message']) 128 | set_versions.assert_not_called() 129 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/resources/test_volumes.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Red Hat, Inc. 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | from unittest import mock 16 | 17 | from oslotest import base 18 | 19 | from sushy_tools.emulator.resources.volumes import StaticDriver 20 | 21 | 22 | @mock.patch('sushy_tools.emulator.resources.volumes.memoize.PersistentDict', 23 | new=dict) 24 | class StaticDriverTestCase(base.BaseTestCase): 25 | 26 | SYSTEM_UUID = "da69abcc-dae0-4913-9a7b-d344043097c0" 27 | STORAGE_ID = "1" 28 | VOLUMES_COL = [ 29 | { 30 | "libvirtPoolName": "sushyPool", 31 | "libvirtVolName": "testVol", 32 | "Id": "1", 33 | "Name": "Sample Volume 1", 34 | "VolumeType": "Mirrored", 35 | "CapacityBytes": 23748 36 | }, 37 | { 38 | "libvirtPoolName": "sushyPool", 39 | "libvirtVolName": "testVol1", 40 | "Id": "2", 41 | "Name": "Sample Volume 2", 42 | "VolumeType": "StripedWithParity", 43 | "CapacityBytes": 48395 44 | } 45 | ] 46 | 47 | CONFIG = { 48 | 'SUSHY_EMULATOR_VOLUMES': { 49 | (SYSTEM_UUID, STORAGE_ID): VOLUMES_COL 50 | } 51 | } 52 | 53 | def setUp(self): 54 | super().setUp() 55 | self.test_driver = StaticDriver(self.CONFIG, mock.MagicMock()) 56 | 57 | def test_get_volumes_col(self): 58 | vol_col = self.test_driver.get_volumes_col(self.SYSTEM_UUID, 59 | self.STORAGE_ID) 60 | self.assertEqual(self.VOLUMES_COL, vol_col) 61 | 62 | def test_add_volume(self): 63 | vol = { 64 | "libvirtPoolName": "sushyPool", 65 | "libvirtVolName": "testVol2", 66 | "Id": "3", 67 | "Name": "Sample Volume 3", 68 | "VolumeType": "Mirrored", 69 | "CapacityBytes": 76584 70 | } 71 | self.test_driver.add_volume(self.SYSTEM_UUID, self.STORAGE_ID, vol) 72 | vol_col = self.test_driver.get_volumes_col(self.SYSTEM_UUID, 73 | self.STORAGE_ID) 74 | self.assertIn(vol, vol_col) 75 | 76 | def test_delete_volume(self): 77 | vol = { 78 | "libvirtPoolName": "sushyPool", 79 | "libvirtVolName": "testVol", 80 | "Id": "1", 81 | "Name": "Sample Volume 1", 82 | "VolumeType": "Mirrored", 83 | "CapacityBytes": 23748 84 | } 85 | self.test_driver.delete_volume(self.SYSTEM_UUID, self.STORAGE_ID, vol) 86 | vol_col = self.test_driver.get_volumes_col(self.SYSTEM_UUID, 87 | self.STORAGE_ID) 88 | self.assertNotIn(vol, vol_col) 89 | -------------------------------------------------------------------------------- /sushy_tools/tests/unit/emulator/test_api_utils.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from unittest import mock 14 | 15 | from sushy_tools.emulator import api_utils 16 | from sushy_tools.emulator import main 17 | from sushy_tools.tests.unit.emulator import test_main 18 | 19 | 20 | class InstanceDeniedTestCase(test_main.EmulatorTestCase): 21 | 22 | def setUp(self): 23 | super().setUp() 24 | ctx = main.app.app_context().__enter__() 25 | self.addCleanup(lambda: ctx.__exit__(None, None, None)) 26 | 27 | @mock.patch.dict(main.app.config, {}, clear=True) 28 | def test_instance_denied_allow_all(self): 29 | self.assertFalse(api_utils.instance_denied(identity='x')) 30 | 31 | @mock.patch.dict( 32 | main.app.config, {'SUSHY_EMULATOR_ALLOWED_INSTANCES': {}}) 33 | def test_instance_denied_disallow_all(self): 34 | self.assertTrue(api_utils.instance_denied(identity='a')) 35 | 36 | def test_instance_denied_undefined_option(self): 37 | with mock.patch.dict(main.app.config): 38 | main.app.config.pop('SUSHY_EMULATOR_ALLOWED_INSTANCES', None) 39 | self.assertFalse(api_utils.instance_denied(identity='a')) 40 | 41 | @mock.patch.dict( 42 | main.app.config, {'SUSHY_EMULATOR_ALLOWED_INSTANCES': {'a'}}) 43 | def test_instance_denied_allow_some(self): 44 | self.assertFalse(api_utils.instance_denied(identity='a')) 45 | 46 | @mock.patch.dict( 47 | main.app.config, {'SUSHY_EMULATOR_ALLOWED_INSTANCES': {'a'}}) 48 | def test_instance_denied_disallow_some(self): 49 | self.assertTrue(api_utils.instance_denied(identity='b')) 50 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | # The order of packages is significant, because pip processes them in the order 2 | # of appearance. Changing the order has an impact on the overall integration 3 | # process, which may cause wedges in the gate later. 4 | 5 | coverage!=4.4,>=4.0 # Apache-2.0 6 | # used by libvirt driver 7 | libvirt-python>=6.0.0 # LGPLv2+ 8 | # used by nova driver 9 | openstacksdk>=0.13.0 # Apache-2.0 10 | oslotest>=3.2.0 # Apache-2.0 11 | stestr>=1.0.0 # Apache-2.0 12 | testtools>=2.2.0 # MIT 13 | munch>=2.1.0 # MIT 14 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 4.4.0 3 | envlist = py3,pep8 4 | ignore_basepython_conflict=true 5 | 6 | [testenv] 7 | constrain_package_deps = true 8 | usedevelop = True 9 | basepython = python3 10 | setenv = 11 | VIRTUAL_ENV={envdir} 12 | PYTHONWARNINGS=default::DeprecationWarning 13 | deps = 14 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 15 | -r{toxinidir}/test-requirements.txt 16 | -r{toxinidir}/requirements.txt 17 | commands = stestr run {posargs} 18 | stestr slowest 19 | 20 | [testenv:pep8] 21 | deps= 22 | hacking~=6.0.0 # Apache-2.0 23 | flake8-import-order>=0.17.1 # LGPLv3 24 | pycodestyle>=2.0.0,<3.0.0 # MIT 25 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 26 | commands = flake8 {posargs} 27 | 28 | [testenv:venv] 29 | commands = {posargs} 30 | 31 | [testenv:cover] 32 | setenv = 33 | {[testenv]setenv} 34 | PYTHON=coverage run --source sushy_tools --parallel-mode 35 | commands = 36 | stestr run {posargs} 37 | coverage combine 38 | coverage html -d cover 39 | coverage xml -o cover/coverage.xml 40 | 41 | [testenv:docs] 42 | deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 43 | -r{toxinidir}/doc/requirements.txt 44 | commands = sphinx-build -W -b html doc/source doc/build/html 45 | 46 | [testenv:pdf-docs] 47 | allowlist_externals = make 48 | deps = {[testenv:docs]deps} 49 | commands = sphinx-build -b latex doc/source doc/build/pdf 50 | make -C doc/build/pdf 51 | 52 | [testenv:releasenotes] 53 | deps = {[testenv:docs]deps} 54 | commands = 55 | sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html 56 | 57 | [testenv:debug] 58 | commands = oslo_debug_helper -t sushy_tools/tests {posargs} 59 | 60 | [flake8] 61 | show-source = True 62 | # E123, E125 skipped as they are invalid PEP-8. 63 | # [W503] Line break occurred before a binary operator. Conflicts with W504. 64 | ignore = E123,E125,W503 65 | builtins = _ 66 | exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build 67 | import-order-style = pep8 68 | application-import-names = sushy_tools 69 | filename = *.py 70 | 71 | [testenv:codespell] 72 | description = 73 | Run codespell to check spelling 74 | deps = codespell 75 | # note(JayF): {posargs} lets us run `tox -ecodespell -- -w` to get codespell 76 | # to correct spelling issues in our code it's aware of. 77 | commands = 78 | codespell {posargs} 79 | -------------------------------------------------------------------------------- /zuul.d/project.yaml: -------------------------------------------------------------------------------- 1 | - project: 2 | templates: 3 | - check-requirements 4 | - openstack-python3-jobs 5 | - publish-openstack-docs-pti 6 | - release-notes-jobs-python3 7 | check: 8 | jobs: 9 | - sushy-tools-tempest-bios-redfish-pxe 10 | - sushy-tools-tempest-uefi-redfish-vmedia 11 | - sushy-tools-tox-codespell: 12 | voting: false 13 | gate: 14 | jobs: 15 | - sushy-tools-tempest-bios-redfish-pxe 16 | - sushy-tools-tempest-uefi-redfish-vmedia 17 | -------------------------------------------------------------------------------- /zuul.d/sushy-tools-jobs.yaml: -------------------------------------------------------------------------------- 1 | - job: 2 | name: sushy-tools-tempest-bios-redfish-pxe 3 | parent: ironic-tempest-bios-redfish-pxe 4 | irrelevant-files: 5 | - ^.*\.rst$ 6 | - ^doc/.*$ 7 | - ^releasenotes/.*$ 8 | - ^setup.cfg$ 9 | - ^test-requirements.txt$ 10 | - ^sushy_tools/tests/.*$ 11 | - ^tox.ini$ 12 | required-projects: 13 | - openstack/sushy-tools 14 | 15 | - job: 16 | name: sushy-tools-tempest-uefi-redfish-vmedia 17 | parent: ironic-tempest-uefi-redfish-vmedia 18 | irrelevant-files: 19 | - ^.*\.rst$ 20 | - ^doc/.*$ 21 | - ^releasenotes/.*$ 22 | - ^setup.cfg$ 23 | - ^test-requirements.txt$ 24 | - ^sushy_tools/tests/.*$ 25 | - ^tox.ini$ 26 | required-projects: 27 | - openstack/sushy-tools 28 | 29 | - job: 30 | name: sushy-tools-tox-codespell 31 | parent: openstack-tox 32 | timeout: 6000 33 | vars: 34 | tox_envlist: codespell --------------------------------------------------------------------------------