blob: 9f759627093cfc16eedc93b7a79b7b531116910e [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
95 """
96 return []
97
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +010098 def get_service_clients(self):
99 """Get a list of the service clients for registration
100
101 If the plugin implements service clients for one or more APIs, it
102 may return their details by this method for automatic registration
103 in any ServiceClients object instantiated by tests.
104 The default implementation returns an empty list.
105
Stephen Finucaned1148042017-03-22 12:35:10 +0000106 :returns: Each element of the list represents the service client for an
107 API. Each dict must define all parameters required for the invocation
108 of `service_clients.ServiceClients.register_service_client_module`.
109 :rtype: list of dictionaries
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100110
Masayuki Igawa683abe22017-04-11 16:06:46 +0900111 Example implementation with one service client::
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100112
Masayuki Igawa683abe22017-04-11 16:06:46 +0900113 def get_service_clients(self):
114 # Example implementation with one service client
115 myservice_config = config.service_client_config('myservice')
116 params = {
117 'name': 'myservice',
118 'service_version': 'myservice',
119 'module_path': 'myservice_tempest_tests.services',
120 'client_names': ['API1Client', 'API2Client'],
121 }
122 params.update(myservice_config)
123 return [params]
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100124
Masayuki Igawa683abe22017-04-11 16:06:46 +0900125 Example implementation with two service clients::
126
127 def get_service_clients(self):
128 # Example implementation with two service clients
129 foo1_config = config.service_client_config('foo')
130 params_foo1 = {
131 'name': 'foo_v1',
132 'service_version': 'foo.v1',
133 'module_path': 'bar_tempest_tests.services.foo.v1',
134 'client_names': ['API1Client', 'API2Client'],
135 }
136 params_foo1.update(foo_config)
137 foo2_config = config.service_client_config('foo')
138 params_foo2 = {
139 'name': 'foo_v2',
140 'service_version': 'foo.v2',
141 'module_path': 'bar_tempest_tests.services.foo.v2',
142 'client_names': ['API1Client', 'API2Client'],
143 }
144 params_foo2.update(foo2_config)
145 return [params_foo1, params_foo2]
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100146 """
147 return []
148
Matthew Treinish7a518772015-07-01 12:46:41 -0400149
150@misc.singleton
151class TempestTestPluginManager(object):
152 """Tempest test plugin manager class
153
154 This class is used to manage the lifecycle of external tempest test
155 plugins. It provides functions for getting set
156 """
157 def __init__(self):
158 self.ext_plugins = stevedore.ExtensionManager(
Marc Koderer191419c2015-07-14 14:30:45 +0200159 'tempest.test_plugins', invoke_on_load=True,
Marc Koderer25319f62015-07-15 11:28:38 +0200160 propagate_map_exceptions=True,
161 on_load_failure_callback=self.failure_hook)
162
163 @staticmethod
164 def failure_hook(_, ep, err):
165 LOG.error('Could not load %r: %s', ep.name, err)
166 raise err
Matthew Treinish7a518772015-07-01 12:46:41 -0400167
168 def get_plugin_load_tests_tuple(self):
169 load_tests_dict = {}
170 for plug in self.ext_plugins:
171 load_tests_dict[plug.name] = plug.obj.load_tests()
172 return load_tests_dict
Matthew Treinisha966d0f2015-07-01 17:37:31 -0400173
174 def register_plugin_opts(self, conf):
175 for plug in self.ext_plugins:
Matthew Treinish1bc49b92015-10-08 11:10:05 -0400176 try:
177 plug.obj.register_opts(conf)
178 except Exception:
179 LOG.exception('Plugin %s raised an exception trying to run '
Jordan Pittier525ec712016-12-07 17:51:26 +0100180 'register_opts', plug.name)
Matthew Treinish83a19aa2015-07-23 13:06:13 -0400181
182 def get_plugin_options_list(self):
183 plugin_options = []
184 for plug in self.ext_plugins:
185 opt_list = plug.obj.get_opt_lists()
186 if opt_list:
187 plugin_options.extend(opt_list)
188 return plugin_options
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100189
190 def _register_service_clients(self):
191 registry = clients.ClientsRegistry()
192 for plug in self.ext_plugins:
193 try:
Matthew Treinish00c72b92016-10-04 13:04:52 -0400194 service_clients = plug.obj.get_service_clients()
195 if service_clients:
196 registry.register_service_client(
197 plug.name, service_clients)
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100198 except Exception:
199 LOG.exception('Plugin %s raised an exception trying to run '
Jordan Pittier525ec712016-12-07 17:51:26 +0100200 'get_service_clients', plug.name)