blob: 613ab92f8585271f4db29492ce54e5eb5086aac9 [file] [log] [blame]
Matthew Treinish7a518772015-07-01 12:46:41 -04001# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# 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, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15import abc
16
Anusha Raminenif3eb9472017-01-13 08:54:01 +053017from oslo_log import log as logging
Matthew Treinish7a518772015-07-01 12:46:41 -040018import six
19import stevedore
Matthew Treinish7a518772015-07-01 12:46:41 -040020
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050021from tempest.lib.common.utils import misc
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +010022from tempest.lib.services import clients
Matthew Treinish7a518772015-07-01 12:46:41 -040023
Marc Koderer25319f62015-07-15 11:28:38 +020024LOG = logging.getLogger(__name__)
25
26
Matthew Treinish7a518772015-07-01 12:46:41 -040027@six.add_metaclass(abc.ABCMeta)
28class TempestPlugin(object):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +000029 """Provide basic hooks for an external plugin
30
31 To provide tempest the necessary information to run the plugin.
Matthew Treinish7a518772015-07-01 12:46:41 -040032 """
33
34 @abc.abstractmethod
35 def load_tests(self):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +000036 """Return the information necessary to load the tests in the plugin.
Matthew Treinish7a518772015-07-01 12:46:41 -040037
38 :return: a tuple with the first value being the test_dir and the second
39 being the top_level
40 :rtype: tuple
41 """
42 return
43
Matthew Treinisha966d0f2015-07-01 17:37:31 -040044 @abc.abstractmethod
45 def register_opts(self, conf):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +000046 """Add additional configuration options to tempest.
47
48 This method will be run for the plugin during the register_opts()
Andrea Frittoli382a6f12017-03-09 11:52:17 +000049 function in tempest.config.
Matthew Treinisha966d0f2015-07-01 17:37:31 -040050
51 :param ConfigOpts conf: The conf object that can be used to register
52 additional options on.
Andrea Frittoli382a6f12017-03-09 11:52:17 +000053
54 Example:
55 >>> # Config options are defined in a config.py module
56 >>> service_option = cfg.BoolOpt(
57 >>> "my_service",
58 >>> default=True,
59 >>> help="Whether or not my service is available")
60 >>>
61 >>> # Note: as long as the group is listed in get_opt_lists,
62 >>> # it will be possible to access its optins in the plugin code
63 >>> # via ("-" in the group name are replaces with "_"):
64 >>> # CONF.my_service.<option_name>
65 >>> my_service_group = cfg.OptGroup(name="my-service",
66 >>> title="My service options")
67 >>>
68 >>> MyServiceGroup = [<list of options>]
69 >>> # (...) More groups and options...
70 >>>
71 >>> # Plugin is implemented in a plugin.py module
72 >>> from my_plugin import config as my_config
73 >>>
74 >>> def register_opts(self, conf):
75 >>> conf.register_opt(my_config.service_option,
76 >>> group='service_available')
77 >>> conf.register_group(my_config.my_service_group)
78 >>> conf.register_opts(my_config.MyService +
79 >>> my_config.my_service_group)
80 >>>
81 >>> conf.register_group(my_config.my_service_feature_group)
82 >>> conf.register_opts(my_config.MyServiceFeaturesGroup,
83 >>> my_config.my_service_feature_group)
Matthew Treinisha966d0f2015-07-01 17:37:31 -040084 """
85 return
86
Matthew Treinish83a19aa2015-07-23 13:06:13 -040087 @abc.abstractmethod
88 def get_opt_lists(self):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +000089 """Get a list of options for sample config generation
Matthew Treinish83a19aa2015-07-23 13:06:13 -040090
91 :return option_list: A list of tuples with the group name and options
92 in that group.
93 :rtype: list
94 """
95 return []
96
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +010097 def get_service_clients(self):
98 """Get a list of the service clients for registration
99
100 If the plugin implements service clients for one or more APIs, it
101 may return their details by this method for automatic registration
102 in any ServiceClients object instantiated by tests.
103 The default implementation returns an empty list.
104
Stephen Finucaned1148042017-03-22 12:35:10 +0000105 :returns: Each element of the list represents the service client for an
106 API. Each dict must define all parameters required for the invocation
107 of `service_clients.ServiceClients.register_service_client_module`.
108 :rtype: list of dictionaries
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100109
110 Example:
111
112 >>> # Example implementation with one service client
113 >>> myservice_config = config.service_client_config('myservice')
114 >>> params = {
115 >>> 'name': 'myservice',
116 >>> 'service_version': 'myservice',
117 >>> 'module_path': 'myservice_tempest_tests.services',
118 >>> 'client_names': ['API1Client', 'API2Client'],
119 >>> }
120 >>> params.update(myservice_config)
Andrea Frittoli (andreaf)e07579c2016-08-05 07:27:02 +0100121 >>> return [params]
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100122
123 >>> # Example implementation with two service clients
124 >>> foo1_config = config.service_client_config('foo')
125 >>> params_foo1 = {
126 >>> 'name': 'foo_v1',
127 >>> 'service_version': 'foo.v1',
128 >>> 'module_path': 'bar_tempest_tests.services.foo.v1',
129 >>> 'client_names': ['API1Client', 'API2Client'],
130 >>> }
131 >>> params_foo1.update(foo_config)
132 >>> foo2_config = config.service_client_config('foo')
133 >>> params_foo2 = {
134 >>> 'name': 'foo_v2',
135 >>> 'service_version': 'foo.v2',
136 >>> 'module_path': 'bar_tempest_tests.services.foo.v2',
137 >>> 'client_names': ['API1Client', 'API2Client'],
138 >>> }
139 >>> params_foo2.update(foo2_config)
Andrea Frittoli (andreaf)e07579c2016-08-05 07:27:02 +0100140 >>> return [params_foo1, params_foo2]
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100141 """
142 return []
143
Matthew Treinish7a518772015-07-01 12:46:41 -0400144
145@misc.singleton
146class TempestTestPluginManager(object):
147 """Tempest test plugin manager class
148
149 This class is used to manage the lifecycle of external tempest test
150 plugins. It provides functions for getting set
151 """
152 def __init__(self):
153 self.ext_plugins = stevedore.ExtensionManager(
Marc Koderer191419c2015-07-14 14:30:45 +0200154 'tempest.test_plugins', invoke_on_load=True,
Marc Koderer25319f62015-07-15 11:28:38 +0200155 propagate_map_exceptions=True,
156 on_load_failure_callback=self.failure_hook)
157
158 @staticmethod
159 def failure_hook(_, ep, err):
160 LOG.error('Could not load %r: %s', ep.name, err)
161 raise err
Matthew Treinish7a518772015-07-01 12:46:41 -0400162
163 def get_plugin_load_tests_tuple(self):
164 load_tests_dict = {}
165 for plug in self.ext_plugins:
166 load_tests_dict[plug.name] = plug.obj.load_tests()
167 return load_tests_dict
Matthew Treinisha966d0f2015-07-01 17:37:31 -0400168
169 def register_plugin_opts(self, conf):
170 for plug in self.ext_plugins:
Matthew Treinish1bc49b92015-10-08 11:10:05 -0400171 try:
172 plug.obj.register_opts(conf)
173 except Exception:
174 LOG.exception('Plugin %s raised an exception trying to run '
Jordan Pittier525ec712016-12-07 17:51:26 +0100175 'register_opts', plug.name)
Matthew Treinish83a19aa2015-07-23 13:06:13 -0400176
177 def get_plugin_options_list(self):
178 plugin_options = []
179 for plug in self.ext_plugins:
180 opt_list = plug.obj.get_opt_lists()
181 if opt_list:
182 plugin_options.extend(opt_list)
183 return plugin_options
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100184
185 def _register_service_clients(self):
186 registry = clients.ClientsRegistry()
187 for plug in self.ext_plugins:
188 try:
Matthew Treinish00c72b92016-10-04 13:04:52 -0400189 service_clients = plug.obj.get_service_clients()
190 if service_clients:
191 registry.register_service_client(
192 plug.name, service_clients)
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100193 except Exception:
194 LOG.exception('Plugin %s raised an exception trying to run '
Jordan Pittier525ec712016-12-07 17:51:26 +0100195 'get_service_clients', plug.name)