blob: 1cfae51b9140272d1ccc7c368bfd64381f05fe3d [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
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030021from tcp_tests.managers import underlay_ssh_manager
22
23LOG = logger.logger
24
25
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030026@pytest.fixture(scope="session")
27def hardware(request, config):
28 """Fixture for manage the hardware layer.
29
30 - start/stop/reboot libvirt/IPMI(/MaaS?) nodes
31 - snapshot/revert libvirt nodes (fuel-devops only)
32 - block/unblock libvirt networks/interfaces (fuel-devops only)
33
34 This fixture should get a hardware configuration from
35 'config' object or create a virtual/baremetal underlay
36 using EnvironmentManager.
37
38 Creates a snapshot 'hardware' with ready-to-use virtual environment
Dennis Dmitriev552454c2019-03-21 23:15:51 +020039 (Only for config.hardware.env_manager='devops'):
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030040 - just created virtual nodes in power-on state
41 - node volumes filled with necessary content
42 - node network interfaces connected to necessary devices
43
Dennis Dmitriev552454c2019-03-21 23:15:51 +020044 config.hardware.env_manager: one of ('devops', 'maas', None)
45 config.hardware.config: path to the config file for the env_manager
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030046 config.hardware.current_snapshot = Latest created or reverted snapshot
47
Dennis Dmitriev552454c2019-03-21 23:15:51 +020048 :rtype EnvironmentModel: if config.hardware.env_manager == 'devops'
49 :rtype EnvironmentManagerEmpty:
50 if config.hardware.env_manager == 'empty'
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030051 """
52 env = None
53
Dennis Dmitriev552454c2019-03-21 23:15:51 +020054 env_manager = config.hardware.env_manager
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030055
Dennis Dmitriev552454c2019-03-21 23:15:51 +020056 if env_manager == 'empty':
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030057 # No environment manager is used.
58 # 'config' should contain config.underlay.ssh settings
59 # 'config' should contain config.underlay.current_snapshot setting
Dennis Dmitrievc902ad82019-04-12 13:41:30 +030060 from tcp_tests.managers import envmanager_empty
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030061 env = envmanager_empty.EnvironmentManagerEmpty(config=config)
62
Dennis Dmitriev552454c2019-03-21 23:15:51 +020063 elif env_manager == 'devops':
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030064 # fuel-devops environment manager is used.
65 # config.underlay.ssh settings can be empty or witn SSH to existing env
66 # config.underlay.current_snapshot
Dennis Dmitrievc902ad82019-04-12 13:41:30 +030067 from tcp_tests.managers import envmanager_devops
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030068 env = envmanager_devops.EnvironmentManager(config=config)
Dennis Dmitrievf5f2e602017-11-03 15:36:19 +020069
Dennis Dmitriev552454c2019-03-21 23:15:51 +020070 elif env_manager == 'heat':
Dennis Dmitrievf5f2e602017-11-03 15:36:19 +020071 # heat environment manager is used.
72 # config.underlay.ssh settings can be empty or witn SSH to existing env
73 # config.underlay.current_snapshot
Dennis Dmitrievc902ad82019-04-12 13:41:30 +030074 from tcp_tests.managers import envmanager_heat
Dennis Dmitrievf5f2e602017-11-03 15:36:19 +020075 env = envmanager_heat.EnvironmentManagerHeat(config=config)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030076 else:
Dennis Dmitriev552454c2019-03-21 23:15:51 +020077 raise Exception("Unknown hardware manager: '{}'".format(env_manager))
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030078
Dennis Dmitriev552454c2019-03-21 23:15:51 +020079 # for devops env_manager: power on nodes and wait for SSH
80 # for empty env_manager: do nothing
81 # for maas env_manager: provision nodes and wait for SSH
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030082 if not env.has_snapshot(ext.SNAPSHOT.hardware):
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030083 env.create_snapshot(ext.SNAPSHOT.hardware)
84
85 def fin():
86 if settings.SHUTDOWN_ENV_ON_TEARDOWN:
87 LOG.info("Shutdown environment...")
88 env.stop()
89
90 request.addfinalizer(fin)
91 return env
92
93
94@pytest.fixture(scope='function')
95def revert_snapshot(request, hardware):
96 """Revert snapshot for the test case
97
98 Usage:
99 @pytest.mark.revert_snapshot(name='<required_snapshot_name>')
100
101 If the mark 'revert_snapshot' is absend, or <required_snapshot_name>
102 not found, then an initial 'hardware' snapshot will be reverted.
103
104 :rtype string: name of the reverted snapshot or None
105 """
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200106 top_fixtures_snapshots = utils.get_top_fixtures_marks(
107 request, 'revert_snapshot')
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300108
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200109 # Try to revert the best matches snapshot for the test
110 for snapshot_name in top_fixtures_snapshots:
111 if hardware.has_snapshot(snapshot_name) and \
112 hardware.has_snapshot_config(snapshot_name):
113 hardware.revert_snapshot(snapshot_name)
114 return snapshot_name
115
116 # Fallback to the basic snapshot
117 hardware.revert_snapshot(ext.SNAPSHOT.hardware)
118 return None
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300119
120
Dennis Dmitriev986326a2018-02-12 17:24:26 +0200121@pytest.fixture(scope='function')
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300122def snapshot(request, hardware):
123 """Fixture for creating snapshot at the end of test if it's needed
124
125 Marks:
126 snapshot_needed(name=None) - make snapshot if test is passed. If
127 name argument provided, it will be used for creating snapshot,
128 otherwise, test function name will be used
129
130 fail_snapshot - make snapshot if test failed
131
132 :param request: pytest.python.FixtureRequest
133 :param env: envmanager.EnvironmentManager
134 """
135 snapshot_needed = request.keywords.get('snapshot_needed', None)
136 fail_snapshot = request.keywords.get('fail_snapshot', None)
137
138 def test_fin():
139 default_snapshot_name = getattr(request.node.function,
140 '_snapshot_name',
141 request.node.function.__name__)
142 if hasattr(request.node, 'rep_call') and request.node.rep_call.passed \
143 and snapshot_needed:
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200144 snapshot_name = utils.extract_name_from_mark(snapshot_needed) or \
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300145 "{}_passed".format(default_snapshot_name)
Dennis Dmitriev411dd102017-09-15 16:04:47 +0300146 hardware.create_snapshot(snapshot_name, force=True)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300147
148 elif hasattr(request.node, 'rep_setup') and \
149 request.node.rep_setup.failed and fail_snapshot:
150 snapshot_name = "{0}_prep_failed".format(default_snapshot_name)
Dennis Dmitriev411dd102017-09-15 16:04:47 +0300151 hardware.create_snapshot(snapshot_name, force=True)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300152
153 elif hasattr(request.node, 'rep_call') and \
154 request.node.rep_call.failed and fail_snapshot:
155 snapshot_name = "{0}_failed".format(default_snapshot_name)
Dennis Dmitriev411dd102017-09-15 16:04:47 +0300156 hardware.create_snapshot(snapshot_name, force=True)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300157
158 request.addfinalizer(test_fin)
159
160
Hanna Arhipova19429962019-10-17 15:16:49 +0300161@pytest.fixture(scope="session")
Dennis Dmitrievb3b37492018-07-08 21:23:00 +0300162def underlay_actions(config):
163 """Fixture that provides SSH access to underlay objects.
164
165 :param config: oslo_config object that keeps various parameters
166 across the fixtures, tests and test runs.
167 All SSH data is taken from the provided config.
168 :rtype UnderlaySSHManager: Object that encapsulate SSH credentials;
169 - provide list of underlay nodes;
170 - provide SSH access to underlay nodes using
171 node names or node IPs.
172 """
173 return underlay_ssh_manager.UnderlaySSHManager(config)
174
175
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200176@pytest.mark.revert_snapshot(ext.SNAPSHOT.underlay)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300177@pytest.fixture(scope="function")
Dennis Dmitrievea214632018-09-19 16:30:52 +0300178def underlay(request, revert_snapshot, config, hardware, underlay_actions):
Dennis Dmitrievb3b37492018-07-08 21:23:00 +0300179 """Fixture that bootstraps the environment underlay.
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300180
181 - Starts the 'hardware' environment and creates 'underlay' with required
182 configuration.
183 - Fills the following object using the 'hardware' fixture:
184 config.underlay.ssh = JSONList of SSH access credentials for nodes.
185 This list will be used for initialization the
186 model UnderlaySSHManager, see it for details.
187
188 :rtype UnderlaySSHManager: Object that encapsulate SSH credentials;
189 - provide list of underlay nodes;
190 - provide SSH access to underlay nodes using
191 node names or node IPs.
192 """
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300193 # Create Underlay
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200194
195 def basic_underlay():
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300196 # If config.underlay.ssh wasn't provided from external config, then
Dennis Dmitriev552454c2019-03-21 23:15:51 +0200197 # try to get necessary data from hardware env_manager (fuel-devops)
Dennis Dmitriev7b9538f2017-05-15 17:01:34 +0300198
Dennis Dmitriev552454c2019-03-21 23:15:51 +0200199 # for devops env_manager: power on nodes and wait for SSH
200 # for empty env_manager: do nothing
201 # for maas env_manager: provision nodes and wait for SSH
Dennis Dmitriev7b9538f2017-05-15 17:01:34 +0300202 hardware.start(underlay_node_roles=config.underlay.roles,
203 timeout=config.underlay.bootstrap_timeout)
204
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300205 config.underlay.ssh = hardware.get_ssh_data(
206 roles=config.underlay.roles)
207
Dmitry Tyzhnenko34595f82018-06-12 19:03:12 +0300208 LOG.info("Config - {}".format(config))
Dennis Dmitrievea214632018-09-19 16:30:52 +0300209 underlay_actions.add_config_ssh(config.underlay.ssh)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300210
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300211 hardware.create_snapshot(ext.SNAPSHOT.underlay)
212
Dennis Dmitrievea214632018-09-19 16:30:52 +0300213 return underlay_actions
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200214
215 def day1_underlay():
216 hardware.start(
217 underlay_node_roles=['salt_master'],
218 timeout=config.underlay.bootstrap_timeout)
219
220 config.underlay.ssh = hardware.get_ssh_data(
221 roles=config.underlay.roles)
Dennis Dmitrievea214632018-09-19 16:30:52 +0300222 underlay_actions.add_config_ssh(config.underlay.ssh)
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200223
224 LOG.info("Generate MACs for MaaS")
225 macs = {
226 n.name.split('.')[0]: {
227 "interface": {
228 "mac": n.get_interface_by_network_name('admin').mac_address}, # noqa
229 "power_parameters": {
230 "power_address": "{}:{}".format(
231 n.get_interface_by_network_name('admin').l2_network_device.address_pool.get_ip('l2_network_device'), # noqa
232 n.bmc_port
233 )}} for n in hardware.slave_nodes}
234
235 config.day1_cfg_config.maas_machines_macs = {
236 "parameters": {
237 "maas": {
238 "region": {
239 "machines": macs}}}}
240
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200241 for node in hardware.slave_nodes:
242 # For correct comissioning by MaaS nodes should be powered off
243 node.destroy()
244
245 hardware.create_snapshot(ext.SNAPSHOT.underlay)
246
Dennis Dmitrievea214632018-09-19 16:30:52 +0300247 return underlay_actions
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200248
249 if not config.underlay.ssh:
250 if request.node.get_marker('day1_underlay'):
Dennis Dmitrievea214632018-09-19 16:30:52 +0300251 return day1_underlay()
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200252 else:
Dennis Dmitrievea214632018-09-19 16:30:52 +0300253 return basic_underlay()
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300254 else:
255 # 1. hardware environment created and powered on
256 # 2. config.underlay.ssh contains SSH access to provisioned nodes
257 # (can be passed from external config with TESTS_CONFIGS variable)
Dennis Dmitrievea214632018-09-19 16:30:52 +0300258 return underlay_actions
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300259
260
Dennis Dmitriev986326a2018-02-12 17:24:26 +0200261@pytest.fixture(scope='function')
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200262def grab_versions(request, func_name, underlay):
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300263 """Fixture for grab package versions at the end of test
264
265 Marks:
266 grab_versions(name=None) - make snapshot if test is passed. If
267 name argument provided, it will be used for creating data,
268 otherwise, test function name will be used
269
270 """
271 grab_version = request.keywords.get('grab_versions', None)
272
273 def test_fin():
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200274 fixture_failed = (hasattr(request.node, 'rep_setup') and
275 request.node.rep_setup.failed)
276 test_passed = (hasattr(request.node, 'rep_call') and
277 request.node.rep_call.passed)
278 test_failed = (hasattr(request.node, 'rep_call') and
279 request.node.rep_call.failed)
280
281 if fixture_failed or test_passed or test_failed:
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300282 artifact_name = utils.extract_name_from_mark(grab_version) or \
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200283 "{}".format(func_name)
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300284 underlay.get_logs(artifact_name)
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200285
286 if grab_version:
287 request.addfinalizer(test_fin)