blob: 1d69d9d297f72d3bcbd7ac0097be858fdb8ea7e5 [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 stevedore
Matthew Treinish7a518772015-07-01 12:46:41 -040019
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050020from tempest.lib.common.utils import misc
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +010021from tempest.lib.services import clients
Matthew Treinish7a518772015-07-01 12:46:41 -040022
Marc Koderer25319f62015-07-15 11:28:38 +020023LOG = logging.getLogger(__name__)
24
25
songwenping4c3bf8f2021-01-05 03:27:09 +000026class TempestPlugin(object, metaclass=abc.ABCMeta):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +000027 """Provide basic hooks for an external plugin
28
29 To provide tempest the necessary information to run the plugin.
Matthew Treinish7a518772015-07-01 12:46:41 -040030 """
31
32 @abc.abstractmethod
33 def load_tests(self):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +000034 """Return the information necessary to load the tests in the plugin.
Matthew Treinish7a518772015-07-01 12:46:41 -040035
36 :return: a tuple with the first value being the test_dir and the second
37 being the top_level
38 :rtype: tuple
39 """
40 return
41
Matthew Treinisha966d0f2015-07-01 17:37:31 -040042 @abc.abstractmethod
43 def register_opts(self, conf):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +000044 """Add additional configuration options to tempest.
45
46 This method will be run for the plugin during the register_opts()
Andrea Frittoli382a6f12017-03-09 11:52:17 +000047 function in tempest.config.
Matthew Treinisha966d0f2015-07-01 17:37:31 -040048
49 :param ConfigOpts conf: The conf object that can be used to register
50 additional options on.
Andrea Frittoli382a6f12017-03-09 11:52:17 +000051
Masayuki Igawa683abe22017-04-11 16:06:46 +090052 Example::
53
54 # Config options are defined in a config.py module
55 service_option = cfg.BoolOpt(
56 "my_service",
57 default=True,
58 help="Whether or not my service is available")
59
60 # Note: as long as the group is listed in get_opt_lists,
61 # it will be possible to access its optins in the plugin code
62 # via ("-" in the group name are replaces with "_"):
63 # CONF.my_service.<option_name>
64 my_service_group = cfg.OptGroup(name="my-service",
65 title="My service options")
66
67 MyServiceGroup = [<list of options>]
68 # (...) More groups and options...
69
70 # Plugin is implemented in a plugin.py module
71 from my_plugin import config as my_config
72
73 def register_opts(self, conf):
74 conf.register_opt(my_config.service_option,
75 group='service_available')
76 conf.register_group(my_config.my_service_group)
Andrea Frittoli (andreaf)17e96e12017-11-05 21:41:33 +110077 conf.register_opts(my_config.MyServiceGroup,
Masayuki Igawa683abe22017-04-11 16:06:46 +090078 my_config.my_service_group)
79
80 conf.register_group(my_config.my_service_feature_group)
81 conf.register_opts(my_config.MyServiceFeaturesGroup,
82 my_config.my_service_feature_group)
Matthew Treinisha966d0f2015-07-01 17:37:31 -040083 """
84 return
85
Matthew Treinish83a19aa2015-07-23 13:06:13 -040086 @abc.abstractmethod
87 def get_opt_lists(self):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +000088 """Get a list of options for sample config generation
Matthew Treinish83a19aa2015-07-23 13:06:13 -040089
90 :return option_list: A list of tuples with the group name and options
91 in that group.
92 :rtype: list
Andrea Frittolif1c68252017-04-24 11:02:39 +010093
94 Example::
95
96 # Config options are defined in a config.py module
97 service_option = cfg.BoolOpt(
98 "my_service", default=True,
99 help="Whether or not my service is available")
100
101 my_service_group = cfg.OptGroup(name="my-service",
102 title="My service options")
103 my_service_features_group = cfg.OptGroup(
104 name="my-service-features",
105 title="My service available features")
106
107 MyServiceGroup = [<list of options>]
108 MyServiceFeaturesGroup = [<list of options>]
109
110 # Plugin is implemented in a plugin.py module
111 from my_plugin import config as my_config
112
113 def get_opt_lists(self, conf):
114 return [
115 (my_service_group.name, MyServiceGroup),
116 (my_service_features_group.name, MyServiceFeaturesGroup)
117 ]
Matthew Treinish83a19aa2015-07-23 13:06:13 -0400118 """
119 return []
120
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100121 def get_service_clients(self):
122 """Get a list of the service clients for registration
123
124 If the plugin implements service clients for one or more APIs, it
125 may return their details by this method for automatic registration
126 in any ServiceClients object instantiated by tests.
127 The default implementation returns an empty list.
128
Stephen Finucaned1148042017-03-22 12:35:10 +0000129 :returns: Each element of the list represents the service client for an
130 API. Each dict must define all parameters required for the invocation
131 of `service_clients.ServiceClients.register_service_client_module`.
132 :rtype: list of dictionaries
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100133
Masayuki Igawa683abe22017-04-11 16:06:46 +0900134 Example implementation with one service client::
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100135
Masayuki Igawa683abe22017-04-11 16:06:46 +0900136 def get_service_clients(self):
137 # Example implementation with one service client
138 myservice_config = config.service_client_config('myservice')
139 params = {
140 'name': 'myservice',
141 'service_version': 'myservice',
142 'module_path': 'myservice_tempest_tests.services',
143 'client_names': ['API1Client', 'API2Client'],
144 }
145 params.update(myservice_config)
146 return [params]
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100147
Masayuki Igawa683abe22017-04-11 16:06:46 +0900148 Example implementation with two service clients::
149
150 def get_service_clients(self):
151 # Example implementation with two service clients
152 foo1_config = config.service_client_config('foo')
153 params_foo1 = {
154 'name': 'foo_v1',
155 'service_version': 'foo.v1',
156 'module_path': 'bar_tempest_tests.services.foo.v1',
157 'client_names': ['API1Client', 'API2Client'],
158 }
159 params_foo1.update(foo_config)
160 foo2_config = config.service_client_config('foo')
161 params_foo2 = {
162 'name': 'foo_v2',
163 'service_version': 'foo.v2',
164 'module_path': 'bar_tempest_tests.services.foo.v2',
165 'client_names': ['API1Client', 'API2Client'],
166 }
167 params_foo2.update(foo2_config)
168 return [params_foo1, params_foo2]
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100169 """
170 return []
171
Matthew Treinish7a518772015-07-01 12:46:41 -0400172
173@misc.singleton
174class TempestTestPluginManager(object):
175 """Tempest test plugin manager class
176
177 This class is used to manage the lifecycle of external tempest test
178 plugins. It provides functions for getting set
179 """
afazekas40fcb9b2019-03-08 11:25:11 +0100180
Matthew Treinish7a518772015-07-01 12:46:41 -0400181 def __init__(self):
182 self.ext_plugins = stevedore.ExtensionManager(
Marc Koderer191419c2015-07-14 14:30:45 +0200183 'tempest.test_plugins', invoke_on_load=True,
Marc Koderer25319f62015-07-15 11:28:38 +0200184 propagate_map_exceptions=True,
185 on_load_failure_callback=self.failure_hook)
186
187 @staticmethod
188 def failure_hook(_, ep, err):
189 LOG.error('Could not load %r: %s', ep.name, err)
190 raise err
Matthew Treinish7a518772015-07-01 12:46:41 -0400191
192 def get_plugin_load_tests_tuple(self):
193 load_tests_dict = {}
194 for plug in self.ext_plugins:
Ghanshyam Mannaa7c1472020-03-10 14:52:22 -0500195 LOG.info('Loading tests from Tempest plugin: %s', plug.name)
Matthew Treinish7a518772015-07-01 12:46:41 -0400196 load_tests_dict[plug.name] = plug.obj.load_tests()
197 return load_tests_dict
Matthew Treinisha966d0f2015-07-01 17:37:31 -0400198
199 def register_plugin_opts(self, conf):
200 for plug in self.ext_plugins:
Ghanshyam Mannaa7c1472020-03-10 14:52:22 -0500201 LOG.info('Register additional config options from Tempest '
202 'plugin: %s', plug.name)
Matthew Treinish1bc49b92015-10-08 11:10:05 -0400203 try:
204 plug.obj.register_opts(conf)
205 except Exception:
206 LOG.exception('Plugin %s raised an exception trying to run '
Jordan Pittier525ec712016-12-07 17:51:26 +0100207 'register_opts', plug.name)
Matthew Treinish83a19aa2015-07-23 13:06:13 -0400208
209 def get_plugin_options_list(self):
210 plugin_options = []
211 for plug in self.ext_plugins:
212 opt_list = plug.obj.get_opt_lists()
Ghanshyam Mannaa7c1472020-03-10 14:52:22 -0500213 LOG.info('List additional config options registered by '
214 'Tempest plugin: %s', plug.name)
215
Matthew Treinish83a19aa2015-07-23 13:06:13 -0400216 if opt_list:
217 plugin_options.extend(opt_list)
218 return plugin_options
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100219
220 def _register_service_clients(self):
221 registry = clients.ClientsRegistry()
222 for plug in self.ext_plugins:
223 try:
Matthew Treinish00c72b92016-10-04 13:04:52 -0400224 service_clients = plug.obj.get_service_clients()
225 if service_clients:
226 registry.register_service_client(
227 plug.name, service_clients)
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100228 except Exception:
229 LOG.exception('Plugin %s raised an exception trying to run '
Jordan Pittier525ec712016-12-07 17:51:26 +0100230 'get_service_clients', plug.name)