blob: abe2b73d545ff07eea499be32606214bf7ba1874 [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()
49 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.
53 """
54 return
55
Matthew Treinish83a19aa2015-07-23 13:06:13 -040056 @abc.abstractmethod
57 def get_opt_lists(self):
Ken'ichi Ohmichi2e2ee192015-11-19 09:48:27 +000058 """Get a list of options for sample config generation
Matthew Treinish83a19aa2015-07-23 13:06:13 -040059
60 :return option_list: A list of tuples with the group name and options
61 in that group.
62 :rtype: list
63 """
64 return []
65
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +010066 def get_service_clients(self):
67 """Get a list of the service clients for registration
68
69 If the plugin implements service clients for one or more APIs, it
70 may return their details by this method for automatic registration
71 in any ServiceClients object instantiated by tests.
72 The default implementation returns an empty list.
73
74 :return list of dictionaries. Each element of the list represents
75 the service client for an API. Each dict must define all
76 parameters required for the invocation of
77 `service_clients.ServiceClients.register_service_client_module`.
78 :rtype: list
79
80 Example:
81
82 >>> # Example implementation with one service client
83 >>> myservice_config = config.service_client_config('myservice')
84 >>> params = {
85 >>> 'name': 'myservice',
86 >>> 'service_version': 'myservice',
87 >>> 'module_path': 'myservice_tempest_tests.services',
88 >>> 'client_names': ['API1Client', 'API2Client'],
89 >>> }
90 >>> params.update(myservice_config)
Andrea Frittoli (andreaf)e07579c2016-08-05 07:27:02 +010091 >>> return [params]
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +010092
93 >>> # Example implementation with two service clients
94 >>> foo1_config = config.service_client_config('foo')
95 >>> params_foo1 = {
96 >>> 'name': 'foo_v1',
97 >>> 'service_version': 'foo.v1',
98 >>> 'module_path': 'bar_tempest_tests.services.foo.v1',
99 >>> 'client_names': ['API1Client', 'API2Client'],
100 >>> }
101 >>> params_foo1.update(foo_config)
102 >>> foo2_config = config.service_client_config('foo')
103 >>> params_foo2 = {
104 >>> 'name': 'foo_v2',
105 >>> 'service_version': 'foo.v2',
106 >>> 'module_path': 'bar_tempest_tests.services.foo.v2',
107 >>> 'client_names': ['API1Client', 'API2Client'],
108 >>> }
109 >>> params_foo2.update(foo2_config)
Andrea Frittoli (andreaf)e07579c2016-08-05 07:27:02 +0100110 >>> return [params_foo1, params_foo2]
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100111 """
112 return []
113
Matthew Treinish7a518772015-07-01 12:46:41 -0400114
115@misc.singleton
116class TempestTestPluginManager(object):
117 """Tempest test plugin manager class
118
119 This class is used to manage the lifecycle of external tempest test
120 plugins. It provides functions for getting set
121 """
122 def __init__(self):
123 self.ext_plugins = stevedore.ExtensionManager(
Marc Koderer191419c2015-07-14 14:30:45 +0200124 'tempest.test_plugins', invoke_on_load=True,
Marc Koderer25319f62015-07-15 11:28:38 +0200125 propagate_map_exceptions=True,
126 on_load_failure_callback=self.failure_hook)
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100127 self._register_service_clients()
Marc Koderer25319f62015-07-15 11:28:38 +0200128
129 @staticmethod
130 def failure_hook(_, ep, err):
131 LOG.error('Could not load %r: %s', ep.name, err)
132 raise err
Matthew Treinish7a518772015-07-01 12:46:41 -0400133
134 def get_plugin_load_tests_tuple(self):
135 load_tests_dict = {}
136 for plug in self.ext_plugins:
137 load_tests_dict[plug.name] = plug.obj.load_tests()
138 return load_tests_dict
Matthew Treinisha966d0f2015-07-01 17:37:31 -0400139
140 def register_plugin_opts(self, conf):
141 for plug in self.ext_plugins:
Matthew Treinish1bc49b92015-10-08 11:10:05 -0400142 try:
143 plug.obj.register_opts(conf)
144 except Exception:
145 LOG.exception('Plugin %s raised an exception trying to run '
Jordan Pittier525ec712016-12-07 17:51:26 +0100146 'register_opts', plug.name)
Matthew Treinish83a19aa2015-07-23 13:06:13 -0400147
148 def get_plugin_options_list(self):
149 plugin_options = []
150 for plug in self.ext_plugins:
151 opt_list = plug.obj.get_opt_lists()
152 if opt_list:
153 plugin_options.extend(opt_list)
154 return plugin_options
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100155
156 def _register_service_clients(self):
157 registry = clients.ClientsRegistry()
158 for plug in self.ext_plugins:
159 try:
Matthew Treinish00c72b92016-10-04 13:04:52 -0400160 service_clients = plug.obj.get_service_clients()
161 if service_clients:
162 registry.register_service_client(
163 plug.name, service_clients)
Andrea Frittoli (andreaf)6d4d85a2016-06-21 17:20:31 +0100164 except Exception:
165 LOG.exception('Plugin %s raised an exception trying to run '
Jordan Pittier525ec712016-12-07 17:51:26 +0100166 'get_service_clients', plug.name)