| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 1 | ============================= | 
|  | 2 | Tempest Test Plugin Interface | 
|  | 3 | ============================= | 
|  | 4 |  | 
|  | 5 | Tempest has an external test plugin interface which enables anyone to integrate | 
|  | 6 | an external test suite as part of a tempest run. This will let any project | 
|  | 7 | leverage being run with the rest of the tempest suite while not requiring the | 
|  | 8 | tests live in the tempest tree. | 
|  | 9 |  | 
|  | 10 | Creating a plugin | 
|  | 11 | ================= | 
|  | 12 |  | 
|  | 13 | Creating a plugin is fairly straightforward and doesn't require much additional | 
| Andrea Frittoli (andreaf) | 1370baf | 2016-04-29 14:26:22 -0500 | [diff] [blame] | 14 | effort on top of creating a test suite using tempest.lib. One thing to note with | 
| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 15 | doing this is that the interfaces exposed by tempest are not considered stable | 
|  | 16 | (with the exception of configuration variables which ever effort goes into | 
|  | 17 | ensuring backwards compatibility). You should not need to import anything from | 
| Kiall Mac Innes | 9e6f974 | 2016-05-23 16:20:55 +0100 | [diff] [blame^] | 18 | tempest itself except where explicitly noted. | 
|  | 19 |  | 
|  | 20 | Stable Tempest APIs plugins may use | 
|  | 21 | ----------------------------------- | 
|  | 22 |  | 
|  | 23 | As noted above, several tempest APIs are acceptable to use from plugins, while | 
|  | 24 | others are not. A list of stable APIs available to plugins is provided below: | 
|  | 25 |  | 
|  | 26 | * tempest.lib.* | 
|  | 27 | * tempest.config | 
|  | 28 | * tempest.test_discover.plugins | 
|  | 29 |  | 
|  | 30 | If there is an interface from tempest that you need to rely on in your plugin | 
|  | 31 | which is not listed above, it likely needs to be migrated to tempest.lib. In | 
|  | 32 | that situation, file a bug, push a migration patch, etc. to expedite providing | 
|  | 33 | the interface in a reliable manner. | 
| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 34 |  | 
| Marc Koderer | 66210aa | 2015-10-26 10:52:32 +0100 | [diff] [blame] | 35 | Plugin Cookiecutter | 
|  | 36 | ------------------- | 
|  | 37 |  | 
|  | 38 | In order to create the basic structure with base classes and test directories | 
|  | 39 | you can use the tempest-plugin-cookiecutter project:: | 
|  | 40 |  | 
| Yuiko Takada | ccb2bbf | 2015-11-17 10:09:44 +0900 | [diff] [blame] | 41 | > pip install -U cookiecutter && cookiecutter https://git.openstack.org/openstack/tempest-plugin-cookiecutter | 
| Marc Koderer | 66210aa | 2015-10-26 10:52:32 +0100 | [diff] [blame] | 42 |  | 
|  | 43 | Cloning into 'tempest-plugin-cookiecutter'... | 
|  | 44 | remote: Counting objects: 17, done. | 
|  | 45 | remote: Compressing objects: 100% (13/13), done. | 
|  | 46 | remote: Total 17 (delta 1), reused 14 (delta 1) | 
|  | 47 | Unpacking objects: 100% (17/17), done. | 
|  | 48 | Checking connectivity... done. | 
|  | 49 | project (default is "sample")? foo | 
|  | 50 | testclass (default is "SampleTempestPlugin")? FooTempestPlugin | 
|  | 51 |  | 
|  | 52 | This would create a folder called ``foo_tempest_plugin/`` with all necessary | 
|  | 53 | basic classes. You only need to move/create your test in | 
|  | 54 | ``foo_tempest_plugin/tests``. | 
|  | 55 |  | 
|  | 56 | Entry Point | 
|  | 57 | ----------- | 
|  | 58 |  | 
|  | 59 | Once you've created your plugin class you need to add an entry point to your | 
|  | 60 | project to enable tempest to find the plugin. The entry point must be added | 
|  | 61 | to the "tempest.test_plugins" namespace. | 
|  | 62 |  | 
|  | 63 | If you are using pbr this is fairly straightforward, in the setup.cfg just add | 
|  | 64 | something like the following:: | 
|  | 65 |  | 
|  | 66 | [entry_points] | 
|  | 67 | tempest.test_plugins = | 
|  | 68 | plugin_name = module.path:PluginClass | 
|  | 69 |  | 
| Matthew Treinish | 00686f2 | 2016-03-09 15:39:19 -0500 | [diff] [blame] | 70 | Standalone Plugin vs In-repo Plugin | 
|  | 71 | ----------------------------------- | 
|  | 72 |  | 
|  | 73 | Since all that's required for a plugin to be detected by tempest is a valid | 
|  | 74 | setuptools entry point in the proper namespace there is no difference from the | 
|  | 75 | tempest perspective on either creating a separate python package to | 
|  | 76 | house the plugin or adding the code to an existing python project. However, | 
|  | 77 | there are tradeoffs to consider when deciding which approach to take when | 
|  | 78 | creating a new plugin. | 
|  | 79 |  | 
|  | 80 | If you create a separate python project for your plugin this makes a lot of | 
|  | 81 | things much easier. Firstly it makes packaging and versioning much simpler, you | 
|  | 82 | can easily decouple the requirements for the plugin from the requirements for | 
|  | 83 | the other project. It lets you version the plugin independently and maintain a | 
|  | 84 | single version of the test code across project release boundaries (see the | 
|  | 85 | `Branchless Tempest Spec`_ for more details on this). It also greatly | 
|  | 86 | simplifies the install time story for external users. Instead of having to | 
|  | 87 | install the right version of a project in the same python namespace as tempest | 
|  | 88 | they simply need to pip install the plugin in that namespace. It also means | 
|  | 89 | that users don't have to worry about inadvertently installing a tempest plugin | 
|  | 90 | when they install another package. | 
|  | 91 |  | 
|  | 92 | .. _Branchless Tempest Spec: http://specs.openstack.org/openstack/qa-specs/specs/tempest/implemented/branchless-tempest.html | 
|  | 93 |  | 
|  | 94 | The sole advantage to integrating a plugin into an existing python project is | 
|  | 95 | that it enables you to land code changes at the same time you land test changes | 
|  | 96 | in the plugin. This reduces some of the burden on contributors by not having | 
|  | 97 | to land 2 changes to add a new API feature and then test it and doing it as a | 
|  | 98 | single combined commit. | 
|  | 99 |  | 
|  | 100 |  | 
| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 101 | Plugin Class | 
| Marc Koderer | 66210aa | 2015-10-26 10:52:32 +0100 | [diff] [blame] | 102 | ============ | 
| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 103 |  | 
|  | 104 | To provide tempest with all the required information it needs to be able to run | 
|  | 105 | your plugin you need to create a plugin class which tempest will load and call | 
|  | 106 | to get information when it needs. To simplify creating this tempest provides an | 
|  | 107 | abstract class that should be used as the parent for your plugin. To use this | 
|  | 108 | you would do something like the following:: | 
|  | 109 |  | 
| YAMAMOTO Takashi | cb2ac6e | 2015-10-19 15:54:42 +0900 | [diff] [blame] | 110 | from tempest.test_discover import plugins | 
| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 111 |  | 
| YAMAMOTO Takashi | cb2ac6e | 2015-10-19 15:54:42 +0900 | [diff] [blame] | 112 | class MyPlugin(plugins.TempestPlugin): | 
| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 113 |  | 
|  | 114 | Then you need to ensure you locally define all of the methods in the abstract | 
|  | 115 | class, you can refer to the api doc below for a reference of what that entails. | 
|  | 116 |  | 
| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 117 | Abstract Plugin Class | 
| Marc Koderer | 66210aa | 2015-10-26 10:52:32 +0100 | [diff] [blame] | 118 | --------------------- | 
| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 119 |  | 
|  | 120 | .. autoclass:: tempest.test_discover.plugins.TempestPlugin | 
|  | 121 | :members: | 
|  | 122 |  | 
| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 123 | Plugin Structure | 
| Marc Koderer | 66210aa | 2015-10-26 10:52:32 +0100 | [diff] [blame] | 124 | ================ | 
| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 125 | While there are no hard and fast rules for the structure a plugin, there are | 
|  | 126 | basically no constraints on what the plugin looks like as long as the 2 steps | 
|  | 127 | above are done. However,  there are some recommended patterns to follow to make | 
|  | 128 | it easy for people to contribute and work with your plugin. For example, if you | 
|  | 129 | create a directory structure with something like:: | 
|  | 130 |  | 
|  | 131 | plugin_dir/ | 
|  | 132 | config.py | 
|  | 133 | plugin.py | 
|  | 134 | tests/ | 
|  | 135 | api/ | 
|  | 136 | scenario/ | 
|  | 137 | services/ | 
|  | 138 | client.py | 
|  | 139 |  | 
|  | 140 | That will mirror what people expect from tempest. The file | 
|  | 141 |  | 
|  | 142 | * **config.py**: contains any plugin specific configuration variables | 
|  | 143 | * **plugin.py**: contains the plugin class used for the entry point | 
|  | 144 | * **tests**: the directory where test discovery will be run, all tests should | 
|  | 145 | be under this dir | 
|  | 146 | * **services**: where the plugin specific service clients are | 
|  | 147 |  | 
|  | 148 | Additionally, when you're creating the plugin you likely want to follow all | 
|  | 149 | of the tempest developer and reviewer documentation to ensure that the tests | 
|  | 150 | being added in the plugin act and behave like the rest of tempest. | 
|  | 151 |  | 
| Matthew Treinish | 9392a83 | 2015-08-24 10:00:49 -0400 | [diff] [blame] | 152 | Dealing with configuration options | 
| Marc Koderer | 66210aa | 2015-10-26 10:52:32 +0100 | [diff] [blame] | 153 | ---------------------------------- | 
| Matthew Treinish | 9392a83 | 2015-08-24 10:00:49 -0400 | [diff] [blame] | 154 |  | 
|  | 155 | Historically Tempest didn't provide external guarantees on its configuration | 
|  | 156 | options. However, with the introduction of the plugin interface this is no | 
|  | 157 | longer the case. An external plugin can rely on using any configuration option | 
|  | 158 | coming from Tempest, there will be at least a full deprecation cycle for any | 
|  | 159 | option before it's removed. However, just the options provided by Tempest | 
|  | 160 | may not be sufficient for the plugin. If you need to add any plugin specific | 
|  | 161 | configuration options you should use the ``register_opts`` and | 
|  | 162 | ``get_opt_lists`` methods to pass them to Tempest when the plugin is loaded. | 
|  | 163 | When adding configuration options the ``register_opts`` method gets passed the | 
|  | 164 | CONF object from tempest. This enables the plugin to add options to both | 
|  | 165 | existing sections and also create new configuration sections for new options. | 
|  | 166 |  | 
| Matthew Treinish | 3a851dc | 2015-07-30 11:34:03 -0400 | [diff] [blame] | 167 | Using Plugins | 
|  | 168 | ============= | 
|  | 169 |  | 
|  | 170 | Tempest will automatically discover any installed plugins when it is run. So by | 
|  | 171 | just installing the python packages which contain your plugin you'll be using | 
|  | 172 | them with tempest, nothing else is really required. | 
|  | 173 |  | 
|  | 174 | However, you should take care when installing plugins. By their very nature | 
|  | 175 | there are no guarantees when running tempest with plugins enabled about the | 
|  | 176 | quality of the plugin. Additionally, while there is no limitation on running | 
|  | 177 | with multiple plugins it's worth noting that poorly written plugins might not | 
|  | 178 | properly isolate their tests which could cause unexpected cross interactions | 
|  | 179 | between plugins. | 
|  | 180 |  | 
|  | 181 | Notes for using plugins with virtualenvs | 
|  | 182 | ---------------------------------------- | 
|  | 183 |  | 
|  | 184 | When using a tempest inside a virtualenv (like when running under tox) you have | 
|  | 185 | to ensure that the package that contains your plugin is either installed in the | 
|  | 186 | venv too or that you have system site-packages enabled. The virtualenv will | 
|  | 187 | isolate the tempest install from the rest of your system so just installing the | 
|  | 188 | plugin package on your system and then running tempest inside a venv will not | 
|  | 189 | work. | 
|  | 190 |  | 
|  | 191 | Tempest also exposes a tox job, all-plugin, which will setup a tox virtualenv | 
|  | 192 | with system site-packages enabled. This will let you leverage tox without | 
|  | 193 | requiring to manually install plugins in the tox venv before running tests. |