From 1fe01a11d47789b9691c46e97aedc827c77326df Mon Sep 17 00:00:00 2001 From: Jay Faulkner Date: Fri, 10 Jun 2016 14:21:26 -0700 Subject: [PATCH] Add example_device hwm and test harnesses Initial hardware manager example, and the requisite testing infrastructure to be able to run pep8 style tests. --- LICENSE | 201 ++++++++++++++++++ example_device_hardware_managers/__init__.py | 0 .../example_device.py | 146 +++++++++++++ requirements.txt | 2 + setup.cfg | 20 ++ setup.py | 6 + test-requirements.txt | 1 + tox.ini | 28 +++ 8 files changed, 404 insertions(+) create mode 100644 LICENSE create mode 100644 example_device_hardware_managers/__init__.py create mode 100644 example_device_hardware_managers/example_device.py create mode 100644 requirements.txt create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 test-requirements.txt create mode 100644 tox.ini diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/example_device_hardware_managers/__init__.py b/example_device_hardware_managers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/example_device_hardware_managers/example_device.py b/example_device_hardware_managers/example_device.py new file mode 100644 index 0000000..ddc150f --- /dev/null +++ b/example_device_hardware_managers/example_device.py @@ -0,0 +1,146 @@ +# Copyright 2015 Rackspace, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ironic_python_agent import hardware +from oslo_log import log + +LOG = log.getLogger() + + +# All the helper methods should be kept outside of the HardwareManager +# so they'll never get accidentally called by dispatch_to_managers() +def _initialize_hardware(): + """Example method for initalizing hardware.""" + # Perform any operations here that are required to initialize your + # hardware. + LOG.debug('Loading drivers, settling udevs, and generally initalizing') + pass + + +def _detect_hardware(): + """Example method for hardware detection.""" + # For this example, return true if hardware is detected, false if not + LOG.debug('Looking for example device') + return True + + +def _is_latest_firmware(): + """Detect if device is running latest firmware.""" + # Actually detect the firmware version instead of returning here. + return True + + +def _upgrade_firmware(): + """Upgrade firmware on device.""" + # Actually perform firmware upgrade instead of returning here. + return True + + +class ExampleDeviceHardwareManager(hardware.HardwareManager): + """Example hardware manager to support a single device""" + + # All hardware managers have a name and a version. + # Version should be bumped anytime a change is introduced. This will + # signal to Ironic that if automatic node cleaning is in progress to + # restart it from the beginning, to ensure consistency. The value can + # be anything; it's checked for equality against previously seen + # name:manager pairs. + HARDWARE_MANAGER_NAME = 'ExampleDeviceHardwareManager' + HARDWARE_MANAGER_VERSION = '1' + + def evaluate_hardware_support(self): + """Declare level of hardware support provided. + + Since this example covers a case of supporting a specific device, + this method is where you would do anything needed to initalize that + device, including loading drivers, and then detect if one exists. + + In some cases, if you expect the hardware to be available on any node + running this hardware manager, or it's undetectable, you may want to + return a static value here. + + Be aware all managers' loaded in IPA will run this method before IPA + performs a lookup or begins heartbeating, so the time needed to + execute this method will make cleaning and deploying slower. + + :returns: HardwareSupport level for this manager. + """ + _initialize_hardware() + if _detect_hardware(): + # This actually resolves down to an int. Upstream IPA will never + # return a value higher than 2 (HardwareSupport.MAINLINE). This + # means your managers should always be SERVICE_PROVIDER or higher. + LOG.debug('Found example device, returning SERVICE_PROVIDER') + return hardware.HardwareSupport.SERVICE_PROVIDER + else: + # If the hardware isn't supported, return HardwareSupport.NONE (0) + # in order to prevent IPA from loading its clean steps or + # attempting to use any methods inside it. + LOG.debug('No example devices found, returning NONE') + return hardware.HardwareSupport.NONE + + def get_clean_steps(self, node, ports): + """Get a list of clean steps with priority. + + Define any clean steps added by this manager here. These will be mixed + with other loaded managers that support this hardware, and ordered by + priority. Higher priority steps run earlier. + + Note that out-of-band clean steps may also be provided by Ironic. + These will follow the same priority ordering even though they are not + executed by IPA. + + There is *no guarantee whatsoever* that steps defined here will be + executed by this HardwareManager. When it comes time to run these + steps, they'll be called using dispatch_to_managers() just like any + other IPA HardwareManager method. This means if they are unique to + your hardware, they should be uniquely named. For example, + upgrade_firmware would be a bad step name. Whereas + upgrade_foobar_device_firmware would be better. + + :param node: The node object as provided by Ironic. + :param ports: Port objects as provided by Ironic. + :returns: A list of cleaning steps, as a list of dicts. + """ + # While obviously you could actively run code here, generally this + # should just return a static value, as any initialization and + # detection should've been done in evaluate_hardware_support(). + return [{ + 'step': 'upgrade_example_device_model1234_firmware', + 'priority': 37, + # If you need Ironic to coordinate a reboot after this step + # runs, but before continuing cleaning, this should be true. + 'reboot_requested': True, + # If it's safe for Ironic to abort cleaning while this step + # runs, this should be true. + 'abortable': False + }] + + def upgrade_example_device_model1234_firmware(self, node, ports): + """Upgrade firmware on Example Device Model #1234.""" + # Any commands needed to perform the firmware upgrade should go here. + # If you plan on actually flashing firmware every cleaning cycle, you + # should ensure your device will not experience flash exhaustion. A + # good practice in some environments would be to check the firmware + # version against a constant in the code, and noop the method if an + # upgrade is not needed. + if _is_latest_firmware(): + LOG.debug('Latest firmware already flashed, skipping') + # Return values are ignored here on success + return True + else: + LOG.debug('Firmware version X found, upgrading to Y') + # Perform firmware upgrade. + _upgrade_firmware() + return True diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..5849519 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +ironic-python-agent +oslo.log diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..d22424a --- /dev/null +++ b/setup.cfg @@ -0,0 +1,20 @@ +[metadata] +name = ipa-example-hardware-managers +author = Jay Faulkner +author-email = jay@jvf.cc +summary = IPA Example Hardware Managers +license = Apache-2 +classifier = + Development Status :: 4 - Beta + Intended Audience :: Developers + Operating System :: OS Independent + License :: OSI Approved :: Apache Software License + Programming Language :: Python + +[files] +packages = + example_device_hardware_manager + +[entry_points] +ironic_python_agent.hardware_managers = + example_device = example_device_hardware_manager.example_device:ExampleDeviceHardwareManager diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..ed58d0f --- /dev/null +++ b/setup.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python +import setuptools + +setuptools.setup( + setup_requires=['pbr'], + pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..ff99a1f --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1 @@ +hacking>=0.10.2,<0.11 diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..945a1a6 --- /dev/null +++ b/tox.ini @@ -0,0 +1,28 @@ +[tox] +minversion = 1.6 +skipsdist = True +envlist = pep8 + +[testenv] +usedevelop = True +install_command = pip install --allow-external -U {opts} {packages} +setenv = VIRTUAL_ENV={envdir} +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +commands = + python setup.py testr --slowest --testr-args='{posargs:}' + +[testenv:pep8] +commands = + flake8 {posargs:example_device_hardware_managers} + +[flake8] +# E711: ignored because it is normal to use "column == None" in sqlalchemy +# H803: "git commit title should not end with period" is silly + +ignore = E12,E711,H803 +builtins = _ +exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,*ironic/nova*,tools + +[hacking] +import_exceptions = ironic.openstack.common.gettextutils._,testtools.matchers