Enhancements to the SNAPS orchestrator/launcher
[snaps.git] / snaps / test_runner.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import argparse
16 import json
17 import logging
18 import unittest
19
20 from snaps import test_suite_builder, file_utils
21 from snaps.openstack.tests import openstack_tests
22
23 __author__ = 'spisarski'
24
25 logger = logging.getLogger('test_runner')
26
27 ARG_NOT_SET = "argument not set"
28 LOG_LEVELS = {'FATAL': logging.FATAL, 'CRITICAL': logging.CRITICAL,
29               'ERROR': logging.ERROR, 'WARN': logging.WARN,
30               'INFO': logging.INFO, 'DEBUG': logging.DEBUG}
31
32
33 def __create_test_suite(
34         source_filename, ext_net_name, proxy_settings, ssh_proxy_cmd,
35         run_unit_tests, run_connection_tests, run_api_tests,
36         run_integration_tests, run_staging_tests, flavor_metadata,
37         image_metadata, use_keystone, use_floating_ips, continuous_integration,
38         log_level):
39     """
40     Compiles the tests that should run
41     :param source_filename: the OpenStack credentials file (required)
42     :param ext_net_name: the name of the external network to use for floating
43                          IPs (required)
44     :param run_unit_tests: when true, the tests not requiring OpenStack will be
45                            added to the test suite
46     :param run_connection_tests: when true, the tests that perform simple
47                                  connections to OpenStack are executed
48     :param run_api_tests: when true, the tests that perform simple API calls to
49                           OpenStack are executed
50     :param run_integration_tests: when true, the integration tests are executed
51     :param run_staging_tests: when true, the staging tests are executed
52     :param proxy_settings: <host>:<port> of the proxy server (optional)
53     :param ssh_proxy_cmd: the command used to connect via SSH over some proxy
54                           server (optional)
55     :param flavor_metadata: dict() object containing the metadata for flavors
56                             created for test VM instance
57     :param image_metadata: dict() object containing the metadata for overriding
58                            default images within the tests
59     :param use_keystone: when true, tests creating users and projects will be
60                          exercised and must be run on a host that
61                          has access to the cloud's administrative network
62     :param use_floating_ips: when true, tests requiring floating IPs will be
63                              executed
64     :param continuous_integration: when true, tests for CI will be run
65     :param log_level: the logging level
66     :return:
67     """
68     suite = unittest.TestSuite()
69
70     os_creds = openstack_tests.get_credentials(
71         os_env_file=source_filename, proxy_settings_str=proxy_settings,
72         ssh_proxy_cmd=ssh_proxy_cmd)
73
74     # Tests that do not require a remote connection to an OpenStack cloud
75     if run_unit_tests:
76         test_suite_builder.add_unit_tests(suite)
77
78     # Basic connection tests
79     if run_connection_tests:
80         test_suite_builder.add_openstack_client_tests(
81             suite=suite, os_creds=os_creds, ext_net_name=ext_net_name,
82             use_keystone=use_keystone, log_level=log_level)
83
84     # Tests the OpenStack API calls
85     if run_api_tests:
86         test_suite_builder.add_openstack_api_tests(
87             suite=suite, os_creds=os_creds, ext_net_name=ext_net_name,
88             use_keystone=use_keystone, image_metadata=image_metadata,
89             log_level=log_level)
90
91     # Long running integration type tests
92     if run_integration_tests:
93         test_suite_builder.add_openstack_integration_tests(
94             suite=suite, os_creds=os_creds, ext_net_name=ext_net_name,
95             use_keystone=use_keystone, flavor_metadata=flavor_metadata,
96             image_metadata=image_metadata, use_floating_ips=use_floating_ips,
97             log_level=log_level)
98
99     if run_staging_tests:
100         test_suite_builder.add_openstack_staging_tests(
101             suite=suite, os_creds=os_creds, ext_net_name=ext_net_name,
102             log_level=log_level)
103
104     if continuous_integration:
105         test_suite_builder.add_openstack_ci_tests(
106             suite=suite, os_creds=os_creds, ext_net_name=ext_net_name,
107             use_keystone=use_keystone, flavor_metadata=flavor_metadata,
108             image_metadata=image_metadata, use_floating_ips=use_floating_ips,
109             log_level=log_level)
110     return suite
111
112
113 def main(arguments):
114     """
115     Begins running unit tests.
116     argv[1] if used must be the source filename else os_env.yaml will be
117     leveraged instead
118     argv[2] if used must be the proxy server <host>:<port>
119     """
120     logger.info('Starting test suite')
121
122     log_level = LOG_LEVELS.get(arguments.log_level, logging.DEBUG)
123
124     flavor_metadata = None
125     if arguments.flavor_metadata:
126         flavor_metadata = json.loads(arguments.flavor_metadata)
127
128     image_metadata = None
129     if arguments.image_metadata_file:
130         image_metadata = file_utils.read_yaml(arguments.image_metadata_file)
131
132     suite = None
133     if arguments.env and arguments.ext_net:
134         unit = arguments.include_unit != ARG_NOT_SET
135         connection = arguments.include_connection != ARG_NOT_SET
136         api = arguments.include_api != ARG_NOT_SET
137         integration = arguments.include_integration != ARG_NOT_SET
138         ci = arguments.continuous_integration != ARG_NOT_SET
139         staging = arguments.include_staging != ARG_NOT_SET
140         if (not unit and not connection and not api and not integration
141                 and not staging and not ci):
142             unit = True
143             connection = True
144             api = True
145             integration = True
146
147         suite = __create_test_suite(
148             arguments.env, arguments.ext_net, arguments.proxy,
149             arguments.ssh_proxy_cmd, unit, connection, api,
150             integration, staging, flavor_metadata, image_metadata,
151             arguments.use_keystone != ARG_NOT_SET,
152             arguments.floating_ips != ARG_NOT_SET,
153             ci, log_level)
154     else:
155         logger.error('Environment file or external network not defined')
156         exit(1)
157
158     i = 0
159     while i < int(arguments.num_runs):
160         result = unittest.TextTestRunner(verbosity=2).run(suite)
161         i += 1
162
163         if result.errors:
164             logger.error('Number of errors in test suite - %s',
165                          len(result.errors))
166             for test, message in result.errors:
167                 logger.error(str(test) + " ERROR with " + message)
168
169         if result.failures:
170             logger.error('Number of failures in test suite - %s',
171                          len(result.failures))
172             for test, message in result.failures:
173                 logger.error(str(test) + " FAILED with " + message)
174
175         if ((result.errors and len(result.errors) > 0)
176                 or (result.failures and len(result.failures) > 0)):
177             logger.error('See above for test failures')
178             exit(1)
179         else:
180             logger.info('All tests completed successfully in run #%s', i)
181
182     logger.info('Successful completion of %s test runs', i)
183     exit(0)
184
185
186 if __name__ == '__main__':
187     parser = argparse.ArgumentParser()
188     parser.add_argument(
189         '-e', '--env', dest='env', required=True,
190         help='OpenStack credentials file')
191     parser.add_argument(
192         '-n', '--net', dest='ext_net', required=True,
193         help='External network name')
194     parser.add_argument(
195         '-p', '--proxy', dest='proxy', nargs='?', default=None,
196         help='Optonal HTTP proxy socket (<host>:<port>)')
197     parser.add_argument(
198         '-s', '--ssh-proxy-cmd', dest='ssh_proxy_cmd', nargs='?', default=None,
199         help='Optonal SSH proxy command value')
200     parser.add_argument(
201         '-l', '--log-level', dest='log_level', default='INFO',
202         help='Logging Level (FATAL|CRITICAL|ERROR|WARN|INFO|DEBUG)')
203     parser.add_argument(
204         '-u', '--unit-tests', dest='include_unit', default=ARG_NOT_SET,
205         nargs='?', help='When argument is set, all tests not requiring '
206                         'OpenStack will be executed')
207     parser.add_argument(
208         '-c', '--connection-tests', dest='include_connection',
209         default=ARG_NOT_SET, nargs='?',
210         help='When argument is set, simple OpenStack connection tests will be '
211              'executed')
212     parser.add_argument(
213         '-a', '--api-tests', dest='include_api', default=ARG_NOT_SET,
214         nargs='?',
215         help='When argument is set, OpenStack API tests will be executed')
216     parser.add_argument(
217         '-i', '--integration-tests', dest='include_integration',
218         default=ARG_NOT_SET, nargs='?',
219         help='When argument is set, OpenStack integrations tests will be '
220              'executed')
221     parser.add_argument(
222         '-st', '--staging-tests', dest='include_staging', default=ARG_NOT_SET,
223         nargs='?',
224         help='When argument is set, OpenStack staging tests will be executed')
225     parser.add_argument(
226         '-f', '--floating-ips', dest='floating_ips', default=ARG_NOT_SET,
227         nargs='?', help='When argument is set, all integration tests requiring'
228                         ' Floating IPs will be executed')
229     parser.add_argument(
230         '-k', '--use-keystone', dest='use_keystone', default=ARG_NOT_SET,
231         nargs='?',
232         help='When argument is set, the tests will exercise the keystone APIs '
233              'and must be run on a machine that has access to the admin '
234              'network and is able to create users and groups')
235     parser.add_argument(
236         '-fm', '--flavor-meta', dest='flavor_metadata',
237         help='JSON string to be used as flavor metadata for all test instances'
238              ' created')
239     parser.add_argument(
240         '-im', '--image-meta', dest='image_metadata_file', default=None,
241         help='Location of YAML file containing the image metadata')
242     parser.add_argument(
243         '-ci', '--continuous-integration', dest='continuous_integration',
244         default=ARG_NOT_SET, nargs='?',
245         help='When argument is set, OpenStack integrations tests will be '
246              'executed')
247     parser.add_argument(
248         '-r', '--num-runs', dest='num_runs', default=1,
249         help='Number of test runs to execute (default 1)')
250
251     args = parser.parse_args()
252
253     main(args)