blob: b1772e828169bb13c00e549096dcf743070216c1 [file] [log] [blame]
Dennis Dmitriev6f59add2016-10-18 13:45:27 +03001# Copyright 2016 Mirantis, Inc.
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 pytest
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030016
17from tcp_tests.helpers import ext
Dennis Dmitriev535869c2016-11-16 22:38:06 +020018from tcp_tests.helpers import utils
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030019from tcp_tests import logger
20from tcp_tests import settings
21from tcp_tests.managers import envmanager_devops
22from tcp_tests.managers import envmanager_empty
Dennis Dmitrievf5f2e602017-11-03 15:36:19 +020023from tcp_tests.managers import envmanager_heat
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030024from tcp_tests.managers import underlay_ssh_manager
25
26LOG = logger.logger
27
28
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030029@pytest.fixture(scope="session")
30def hardware(request, config):
31 """Fixture for manage the hardware layer.
32
33 - start/stop/reboot libvirt/IPMI(/MaaS?) nodes
34 - snapshot/revert libvirt nodes (fuel-devops only)
35 - block/unblock libvirt networks/interfaces (fuel-devops only)
36
37 This fixture should get a hardware configuration from
38 'config' object or create a virtual/baremetal underlay
39 using EnvironmentManager.
40
41 Creates a snapshot 'hardware' with ready-to-use virtual environment
42 (Only for config.hardware.manager='devops'):
43 - just created virtual nodes in power-on state
44 - node volumes filled with necessary content
45 - node network interfaces connected to necessary devices
46
47 config.hardware.manager: one of ('devops', 'maas', None)
48 config.hardware.config: path to the config file for the manager
49 config.hardware.current_snapshot = Latest created or reverted snapshot
50
51 :rtype EnvironmentModel: if config.hardware.manager == 'devops'
52 :rtype EnvironmentManagerEmpty: if config.hardware.manager == 'empty'
53 """
54 env = None
55
56 manager = config.hardware.manager
57
58 if manager == 'empty':
59 # No environment manager is used.
60 # 'config' should contain config.underlay.ssh settings
61 # 'config' should contain config.underlay.current_snapshot setting
62 env = envmanager_empty.EnvironmentManagerEmpty(config=config)
63
64 elif manager == 'devops':
65 # fuel-devops environment manager is used.
66 # config.underlay.ssh settings can be empty or witn SSH to existing env
67 # config.underlay.current_snapshot
68 env = envmanager_devops.EnvironmentManager(config=config)
Dennis Dmitrievf5f2e602017-11-03 15:36:19 +020069
70 elif manager == 'heat':
71 # heat environment manager is used.
72 # config.underlay.ssh settings can be empty or witn SSH to existing env
73 # config.underlay.current_snapshot
74 env = envmanager_heat.EnvironmentManagerHeat(config=config)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030075 else:
76 raise Exception("Unknown hardware manager: '{}'".format(manager))
77
78 # for devops manager: power on nodes and wait for SSH
79 # for empty manager: do nothing
80 # for maas manager: provision nodes and wait for SSH
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030081 if not env.has_snapshot(ext.SNAPSHOT.hardware):
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030082 env.create_snapshot(ext.SNAPSHOT.hardware)
83
84 def fin():
85 if settings.SHUTDOWN_ENV_ON_TEARDOWN:
86 LOG.info("Shutdown environment...")
87 env.stop()
88
89 request.addfinalizer(fin)
90 return env
91
92
93@pytest.fixture(scope='function')
94def revert_snapshot(request, hardware):
95 """Revert snapshot for the test case
96
97 Usage:
98 @pytest.mark.revert_snapshot(name='<required_snapshot_name>')
99
100 If the mark 'revert_snapshot' is absend, or <required_snapshot_name>
101 not found, then an initial 'hardware' snapshot will be reverted.
102
103 :rtype string: name of the reverted snapshot or None
104 """
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200105 top_fixtures_snapshots = utils.get_top_fixtures_marks(
106 request, 'revert_snapshot')
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300107
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200108 # Try to revert the best matches snapshot for the test
109 for snapshot_name in top_fixtures_snapshots:
110 if hardware.has_snapshot(snapshot_name) and \
111 hardware.has_snapshot_config(snapshot_name):
112 hardware.revert_snapshot(snapshot_name)
113 return snapshot_name
114
115 # Fallback to the basic snapshot
116 hardware.revert_snapshot(ext.SNAPSHOT.hardware)
117 return None
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300118
119
Dennis Dmitriev986326a2018-02-12 17:24:26 +0200120@pytest.fixture(scope='function')
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300121def snapshot(request, hardware):
122 """Fixture for creating snapshot at the end of test if it's needed
123
124 Marks:
125 snapshot_needed(name=None) - make snapshot if test is passed. If
126 name argument provided, it will be used for creating snapshot,
127 otherwise, test function name will be used
128
129 fail_snapshot - make snapshot if test failed
130
131 :param request: pytest.python.FixtureRequest
132 :param env: envmanager.EnvironmentManager
133 """
134 snapshot_needed = request.keywords.get('snapshot_needed', None)
135 fail_snapshot = request.keywords.get('fail_snapshot', None)
136
137 def test_fin():
138 default_snapshot_name = getattr(request.node.function,
139 '_snapshot_name',
140 request.node.function.__name__)
141 if hasattr(request.node, 'rep_call') and request.node.rep_call.passed \
142 and snapshot_needed:
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200143 snapshot_name = utils.extract_name_from_mark(snapshot_needed) or \
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300144 "{}_passed".format(default_snapshot_name)
Dennis Dmitriev411dd102017-09-15 16:04:47 +0300145 hardware.create_snapshot(snapshot_name, force=True)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300146
147 elif hasattr(request.node, 'rep_setup') and \
148 request.node.rep_setup.failed and fail_snapshot:
149 snapshot_name = "{0}_prep_failed".format(default_snapshot_name)
Dennis Dmitriev411dd102017-09-15 16:04:47 +0300150 hardware.create_snapshot(snapshot_name, force=True)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300151
152 elif hasattr(request.node, 'rep_call') and \
153 request.node.rep_call.failed and fail_snapshot:
154 snapshot_name = "{0}_failed".format(default_snapshot_name)
Dennis Dmitriev411dd102017-09-15 16:04:47 +0300155 hardware.create_snapshot(snapshot_name, force=True)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300156
157 request.addfinalizer(test_fin)
158
159
Dennis Dmitrievb3b37492018-07-08 21:23:00 +0300160@pytest.fixture(scope="function")
161def underlay_actions(config):
162 """Fixture that provides SSH access to underlay objects.
163
164 :param config: oslo_config object that keeps various parameters
165 across the fixtures, tests and test runs.
166 All SSH data is taken from the provided config.
167 :rtype UnderlaySSHManager: Object that encapsulate SSH credentials;
168 - provide list of underlay nodes;
169 - provide SSH access to underlay nodes using
170 node names or node IPs.
171 """
172 return underlay_ssh_manager.UnderlaySSHManager(config)
173
174
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200175@pytest.mark.revert_snapshot(ext.SNAPSHOT.underlay)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300176@pytest.fixture(scope="function")
Dennis Dmitrievea214632018-09-19 16:30:52 +0300177def underlay(request, revert_snapshot, config, hardware, underlay_actions):
Dennis Dmitrievb3b37492018-07-08 21:23:00 +0300178 """Fixture that bootstraps the environment underlay.
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300179
180 - Starts the 'hardware' environment and creates 'underlay' with required
181 configuration.
182 - Fills the following object using the 'hardware' fixture:
183 config.underlay.ssh = JSONList of SSH access credentials for nodes.
184 This list will be used for initialization the
185 model UnderlaySSHManager, see it for details.
186
187 :rtype UnderlaySSHManager: Object that encapsulate SSH credentials;
188 - provide list of underlay nodes;
189 - provide SSH access to underlay nodes using
190 node names or node IPs.
191 """
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300192 # Create Underlay
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200193
194 def basic_underlay():
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300195 # If config.underlay.ssh wasn't provided from external config, then
196 # try to get necessary data from hardware manager (fuel-devops)
Dennis Dmitriev7b9538f2017-05-15 17:01:34 +0300197
198 # for devops manager: power on nodes and wait for SSH
199 # for empty manager: do nothing
200 # for maas manager: provision nodes and wait for SSH
201 hardware.start(underlay_node_roles=config.underlay.roles,
202 timeout=config.underlay.bootstrap_timeout)
203
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300204 config.underlay.ssh = hardware.get_ssh_data(
205 roles=config.underlay.roles)
206
Dmitry Tyzhnenko34595f82018-06-12 19:03:12 +0300207 LOG.info("Config - {}".format(config))
Dennis Dmitrievea214632018-09-19 16:30:52 +0300208 underlay_actions.add_config_ssh(config.underlay.ssh)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300209
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300210 hardware.create_snapshot(ext.SNAPSHOT.underlay)
211
Dennis Dmitrievea214632018-09-19 16:30:52 +0300212 return underlay_actions
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200213
214 def day1_underlay():
215 hardware.start(
216 underlay_node_roles=['salt_master'],
217 timeout=config.underlay.bootstrap_timeout)
218
219 config.underlay.ssh = hardware.get_ssh_data(
220 roles=config.underlay.roles)
Dennis Dmitrievea214632018-09-19 16:30:52 +0300221 underlay_actions.add_config_ssh(config.underlay.ssh)
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200222
223 LOG.info("Generate MACs for MaaS")
224 macs = {
225 n.name.split('.')[0]: {
226 "interface": {
227 "mac": n.get_interface_by_network_name('admin').mac_address}, # noqa
228 "power_parameters": {
229 "power_address": "{}:{}".format(
230 n.get_interface_by_network_name('admin').l2_network_device.address_pool.get_ip('l2_network_device'), # noqa
231 n.bmc_port
232 )}} for n in hardware.slave_nodes}
233
234 config.day1_cfg_config.maas_machines_macs = {
235 "parameters": {
236 "maas": {
237 "region": {
238 "machines": macs}}}}
239
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200240 for node in hardware.slave_nodes:
241 # For correct comissioning by MaaS nodes should be powered off
242 node.destroy()
243
244 hardware.create_snapshot(ext.SNAPSHOT.underlay)
245
Dennis Dmitrievea214632018-09-19 16:30:52 +0300246 return underlay_actions
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200247
248 if not config.underlay.ssh:
249 if request.node.get_marker('day1_underlay'):
Dennis Dmitrievea214632018-09-19 16:30:52 +0300250 return day1_underlay()
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200251 else:
Dennis Dmitrievea214632018-09-19 16:30:52 +0300252 return basic_underlay()
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300253 else:
254 # 1. hardware environment created and powered on
255 # 2. config.underlay.ssh contains SSH access to provisioned nodes
256 # (can be passed from external config with TESTS_CONFIGS variable)
Dennis Dmitrievea214632018-09-19 16:30:52 +0300257 return underlay_actions
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300258
259
Dennis Dmitriev986326a2018-02-12 17:24:26 +0200260@pytest.fixture(scope='function')
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200261def grab_versions(request, func_name, underlay):
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300262 """Fixture for grab package versions at the end of test
263
264 Marks:
265 grab_versions(name=None) - make snapshot if test is passed. If
266 name argument provided, it will be used for creating data,
267 otherwise, test function name will be used
268
269 """
270 grab_version = request.keywords.get('grab_versions', None)
271
272 def test_fin():
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200273 fixture_failed = (hasattr(request.node, 'rep_setup') and
274 request.node.rep_setup.failed)
275 test_passed = (hasattr(request.node, 'rep_call') and
276 request.node.rep_call.passed)
277 test_failed = (hasattr(request.node, 'rep_call') and
278 request.node.rep_call.failed)
279
280 if fixture_failed or test_passed or test_failed:
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300281 artifact_name = utils.extract_name_from_mark(grab_version) or \
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200282 "{}".format(func_name)
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300283 underlay.get_logs(artifact_name)
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200284
285 if grab_version:
286 request.addfinalizer(test_fin)