blob: 1206e3feefd4d4ff590a8309845f52f674f010c3 [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
Masayuki Igawa683abe22017-04-11 16:06:46 +090054 Example::
55
56 # Config options are defined in a config.py module
57 service_option = cfg.BoolOpt(
58 "my_service",
59 default=True,
60 help="Whether or not my service is available")
61
62 # Note: as long as the group is listed in get_opt_lists,
63 # it will be possible to access its optins in the plugin code
64 # via ("-" in the group name are replaces with "_"):
65 # CONF.my_service.<option_name>
66 my_service_group = cfg.OptGroup(name="my-service",
67 title="My service options")
68
69 MyServiceGroup = [<list of options>]
70 # (...) More groups and options...
71
72 # Plugin is implemented in a plugin.py module
73 from my_plugin import config as my_config
74
75 def register_opts(self, conf):
76 conf.register_opt(my_config.service_option,
77 group='service_available')
78 conf.register_group(my_config.my_service_group)
79 conf.register_opts(my_config.MyService +
80 my_config.my_service_group)
81
82 conf.register_group(my_config.my_service_feature_group)
83 conf.register_opts(my_config.MyServiceFeaturesGroup,
84 my_config.my_service_feature_group)
Matthew Treinisha966d0f2015-07-01 17:37:31 -040085 """
86 return
87
Matthew Treinish83a19aa2015-07-23 13:06:13 -040088 @abc.abstractmethod
89 def get_opt_lists(self):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +000090 """Get a list of options for sample config generation
Matthew Treinish83a19aa2015-07-23 13:06:13 -040091
92 :return option_list: A list of tuples with the group name and options
93 in that group.
94 :rtype: list
Andrea Frittolif1c68252017-04-24 11:02:39 +010095
96 Example::
97
98 # Config options are defined in a config.py module
99 service_option = cfg.BoolOpt(
100 "my_service", default=True,
101 help="Whether or not my service is available")
102
103 my_service_group = cfg.OptGroup(name="my-service",
104 title="My service options")
105 my_service_features_group = cfg.OptGroup(
106 name="my-service-features",
107 title="My service available features")
108
109 MyServiceGroup = [<list of options>]
110 MyServiceFeaturesGroup = [<list of options>]
111
112 # Plugin is implemented in a plugin.py module
113 from my_plugin import config as my_config
114
115 def get_opt_lists(self, conf):
116 return [
117 (my_service_group.name, MyServiceGroup),
118 (my_service_features_group.name, MyServiceFeaturesGroup)
119 ]
Matthew Treinish83a19aa2015-07-23 13:06:13 -0400120 """
121 return []
122
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100123 def get_service_clients(self):
124 """Get a list of the service clients for registration
125
126 If the plugin implements service clients for one or more APIs, it
127 may return their details by this method for automatic registration
128 in any ServiceClients object instantiated by tests.
129 The default implementation returns an empty list.
130
Stephen Finucaned1148042017-03-22 12:35:10 +0000131 :returns: Each element of the list represents the service client for an
132 API. Each dict must define all parameters required for the invocation
133 of `service_clients.ServiceClients.register_service_client_module`.
134 :rtype: list of dictionaries
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100135
Masayuki Igawa683abe22017-04-11 16:06:46 +0900136 Example implementation with one service client::
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100137
Masayuki Igawa683abe22017-04-11 16:06:46 +0900138 def get_service_clients(self):
139 # Example implementation with one service client
140 myservice_config = config.service_client_config('myservice')
141 params = {
142 'name': 'myservice',
143 'service_version': 'myservice',
144 'module_path': 'myservice_tempest_tests.services',
145 'client_names': ['API1Client', 'API2Client'],
146 }
147 params.update(myservice_config)
148 return [params]
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100149
Masayuki Igawa683abe22017-04-11 16:06:46 +0900150 Example implementation with two service clients::
151
152 def get_service_clients(self):
153 # Example implementation with two service clients
154 foo1_config = config.service_client_config('foo')
155 params_foo1 = {
156 'name': 'foo_v1',
157 'service_version': 'foo.v1',
158 'module_path': 'bar_tempest_tests.services.foo.v1',
159 'client_names': ['API1Client', 'API2Client'],
160 }
161 params_foo1.update(foo_config)
162 foo2_config = config.service_client_config('foo')
163 params_foo2 = {
164 'name': 'foo_v2',
165 'service_version': 'foo.v2',
166 'module_path': 'bar_tempest_tests.services.foo.v2',
167 'client_names': ['API1Client', 'API2Client'],
168 }
169 params_foo2.update(foo2_config)
170 return [params_foo1, params_foo2]
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100171 """
172 return []
173
Matthew Treinish7a518772015-07-01 12:46:41 -0400174
175@misc.singleton
176class TempestTestPluginManager(object):
177 """Tempest test plugin manager class
178
179 This class is used to manage the lifecycle of external tempest test
180 plugins. It provides functions for getting set
181 """
182 def __init__(self):
183 self.ext_plugins = stevedore.ExtensionManager(
Marc Koderer191419c2015-07-14 14:30:45 +0200184 'tempest.test_plugins', invoke_on_load=True,
Marc Koderer25319f62015-07-15 11:28:38 +0200185 propagate_map_exceptions=True,
186 on_load_failure_callback=self.failure_hook)
187
188 @staticmethod
189 def failure_hook(_, ep, err):
190 LOG.error('Could not load %r: %s', ep.name, err)
191 raise err
Matthew Treinish7a518772015-07-01 12:46:41 -0400192
193 def get_plugin_load_tests_tuple(self):
194 load_tests_dict = {}
195 for plug in self.ext_plugins:
196 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:
Matthew Treinish1bc49b92015-10-08 11:10:05 -0400201 try:
202 plug.obj.register_opts(conf)
203 except Exception:
204 LOG.exception('Plugin %s raised an exception trying to run '
Jordan Pittier525ec712016-12-07 17:51:26 +0100205 'register_opts', plug.name)
Matthew Treinish83a19aa2015-07-23 13:06:13 -0400206
207 def get_plugin_options_list(self):
208 plugin_options = []
209 for plug in self.ext_plugins:
210 opt_list = plug.obj.get_opt_lists()
211 if opt_list:
212 plugin_options.extend(opt_list)
213 return plugin_options
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100214
215 def _register_service_clients(self):
216 registry = clients.ClientsRegistry()
217 for plug in self.ext_plugins:
218 try:
Matthew Treinish00c72b92016-10-04 13:04:52 -0400219 service_clients = plug.obj.get_service_clients()
220 if service_clients:
221 registry.register_service_client(
222 plug.name, service_clients)
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100223 except Exception:
224 LOG.exception('Plugin %s raised an exception trying to run '
Jordan Pittier525ec712016-12-07 17:51:26 +0100225 'get_service_clients', plug.name)