blob: 4e2fa95b1a45882ef7f1ec4b101c8b600a2434ba [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
23from tcp_tests.managers import underlay_ssh_manager
24
25LOG = logger.logger
26
27
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030028@pytest.fixture(scope="session")
29def hardware(request, config):
30 """Fixture for manage the hardware layer.
31
32 - start/stop/reboot libvirt/IPMI(/MaaS?) nodes
33 - snapshot/revert libvirt nodes (fuel-devops only)
34 - block/unblock libvirt networks/interfaces (fuel-devops only)
35
36 This fixture should get a hardware configuration from
37 'config' object or create a virtual/baremetal underlay
38 using EnvironmentManager.
39
40 Creates a snapshot 'hardware' with ready-to-use virtual environment
41 (Only for config.hardware.manager='devops'):
42 - just created virtual nodes in power-on state
43 - node volumes filled with necessary content
44 - node network interfaces connected to necessary devices
45
46 config.hardware.manager: one of ('devops', 'maas', None)
47 config.hardware.config: path to the config file for the manager
48 config.hardware.current_snapshot = Latest created or reverted snapshot
49
50 :rtype EnvironmentModel: if config.hardware.manager == 'devops'
51 :rtype EnvironmentManagerEmpty: if config.hardware.manager == 'empty'
52 """
53 env = None
54
55 manager = config.hardware.manager
56
57 if manager == 'empty':
58 # No environment manager is used.
59 # 'config' should contain config.underlay.ssh settings
60 # 'config' should contain config.underlay.current_snapshot setting
61 env = envmanager_empty.EnvironmentManagerEmpty(config=config)
62
63 elif manager == 'devops':
64 # 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
67 env = envmanager_devops.EnvironmentManager(config=config)
68 else:
69 raise Exception("Unknown hardware manager: '{}'".format(manager))
70
71 # for devops manager: power on nodes and wait for SSH
72 # for empty manager: do nothing
73 # for maas manager: provision nodes and wait for SSH
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030074 if not env.has_snapshot(ext.SNAPSHOT.hardware):
Dennis Dmitriev6f59add2016-10-18 13:45:27 +030075 env.create_snapshot(ext.SNAPSHOT.hardware)
76
77 def fin():
78 if settings.SHUTDOWN_ENV_ON_TEARDOWN:
79 LOG.info("Shutdown environment...")
80 env.stop()
81
82 request.addfinalizer(fin)
83 return env
84
85
86@pytest.fixture(scope='function')
87def revert_snapshot(request, hardware):
88 """Revert snapshot for the test case
89
90 Usage:
91 @pytest.mark.revert_snapshot(name='<required_snapshot_name>')
92
93 If the mark 'revert_snapshot' is absend, or <required_snapshot_name>
94 not found, then an initial 'hardware' snapshot will be reverted.
95
96 :rtype string: name of the reverted snapshot or None
97 """
Dennis Dmitriev535869c2016-11-16 22:38:06 +020098 top_fixtures_snapshots = utils.get_top_fixtures_marks(
99 request, 'revert_snapshot')
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300100
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200101 # Try to revert the best matches snapshot for the test
102 for snapshot_name in top_fixtures_snapshots:
103 if hardware.has_snapshot(snapshot_name) and \
104 hardware.has_snapshot_config(snapshot_name):
105 hardware.revert_snapshot(snapshot_name)
106 return snapshot_name
107
108 # Fallback to the basic snapshot
109 hardware.revert_snapshot(ext.SNAPSHOT.hardware)
110 return None
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300111
112
Dennis Dmitriev986326a2018-02-12 17:24:26 +0200113@pytest.fixture(scope='function')
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300114def snapshot(request, hardware):
115 """Fixture for creating snapshot at the end of test if it's needed
116
117 Marks:
118 snapshot_needed(name=None) - make snapshot if test is passed. If
119 name argument provided, it will be used for creating snapshot,
120 otherwise, test function name will be used
121
122 fail_snapshot - make snapshot if test failed
123
124 :param request: pytest.python.FixtureRequest
125 :param env: envmanager.EnvironmentManager
126 """
127 snapshot_needed = request.keywords.get('snapshot_needed', None)
128 fail_snapshot = request.keywords.get('fail_snapshot', None)
129
130 def test_fin():
131 default_snapshot_name = getattr(request.node.function,
132 '_snapshot_name',
133 request.node.function.__name__)
134 if hasattr(request.node, 'rep_call') and request.node.rep_call.passed \
135 and snapshot_needed:
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200136 snapshot_name = utils.extract_name_from_mark(snapshot_needed) or \
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300137 "{}_passed".format(default_snapshot_name)
Dennis Dmitriev411dd102017-09-15 16:04:47 +0300138 hardware.create_snapshot(snapshot_name, force=True)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300139
140 elif hasattr(request.node, 'rep_setup') and \
141 request.node.rep_setup.failed and fail_snapshot:
142 snapshot_name = "{0}_prep_failed".format(default_snapshot_name)
Dennis Dmitriev411dd102017-09-15 16:04:47 +0300143 hardware.create_snapshot(snapshot_name, force=True)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300144
145 elif hasattr(request.node, 'rep_call') and \
146 request.node.rep_call.failed and fail_snapshot:
147 snapshot_name = "{0}_failed".format(default_snapshot_name)
Dennis Dmitriev411dd102017-09-15 16:04:47 +0300148 hardware.create_snapshot(snapshot_name, force=True)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300149
150 request.addfinalizer(test_fin)
151
152
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200153@pytest.mark.revert_snapshot(ext.SNAPSHOT.underlay)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300154@pytest.fixture(scope="function")
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200155def underlay(request, revert_snapshot, config, hardware):
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300156 """Fixture that should provide SSH access to underlay objects.
157
158 - Starts the 'hardware' environment and creates 'underlay' with required
159 configuration.
160 - Fills the following object using the 'hardware' fixture:
161 config.underlay.ssh = JSONList of SSH access credentials for nodes.
162 This list will be used for initialization the
163 model UnderlaySSHManager, see it for details.
164
165 :rtype UnderlaySSHManager: Object that encapsulate SSH credentials;
166 - provide list of underlay nodes;
167 - provide SSH access to underlay nodes using
168 node names or node IPs.
169 """
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300170 # Create Underlay
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200171
172 def basic_underlay():
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300173 # If config.underlay.ssh wasn't provided from external config, then
174 # try to get necessary data from hardware manager (fuel-devops)
Dennis Dmitriev7b9538f2017-05-15 17:01:34 +0300175
176 # for devops manager: power on nodes and wait for SSH
177 # for empty manager: do nothing
178 # for maas manager: provision nodes and wait for SSH
179 hardware.start(underlay_node_roles=config.underlay.roles,
180 timeout=config.underlay.bootstrap_timeout)
181
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300182 config.underlay.ssh = hardware.get_ssh_data(
183 roles=config.underlay.roles)
184
Dmitry Tyzhnenko34595f82018-06-12 19:03:12 +0300185 LOG.info("Config - {}".format(config))
Dennis Dmitriev2a13a132016-11-04 00:56:23 +0200186 underlay = underlay_ssh_manager.UnderlaySSHManager(config)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300187
188 if not config.underlay.lvm:
189 underlay.enable_lvm(hardware.lvm_storages())
190 config.underlay.lvm = underlay.config_lvm
191
192 hardware.create_snapshot(ext.SNAPSHOT.underlay)
193
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200194 return underlay
195
196 def day1_underlay():
197 hardware.start(
198 underlay_node_roles=['salt_master'],
199 timeout=config.underlay.bootstrap_timeout)
200
201 config.underlay.ssh = hardware.get_ssh_data(
202 roles=config.underlay.roles)
203
204 underlay = underlay_ssh_manager.UnderlaySSHManager(config)
205
206 LOG.info("Generate MACs for MaaS")
207 macs = {
208 n.name.split('.')[0]: {
209 "interface": {
210 "mac": n.get_interface_by_network_name('admin').mac_address}, # noqa
211 "power_parameters": {
212 "power_address": "{}:{}".format(
213 n.get_interface_by_network_name('admin').l2_network_device.address_pool.get_ip('l2_network_device'), # noqa
214 n.bmc_port
215 )}} for n in hardware.slave_nodes}
216
217 config.day1_cfg_config.maas_machines_macs = {
218 "parameters": {
219 "maas": {
220 "region": {
221 "machines": macs}}}}
222
223 if not config.underlay.lvm:
224 underlay.enable_lvm(hardware.lvm_storages())
225 config.underlay.lvm = underlay.config_lvm
226
227 for node in hardware.slave_nodes:
228 # For correct comissioning by MaaS nodes should be powered off
229 node.destroy()
230
231 hardware.create_snapshot(ext.SNAPSHOT.underlay)
232
233 return underlay
234
235 if not config.underlay.ssh:
236 if request.node.get_marker('day1_underlay'):
237 underlay = day1_underlay()
238 else:
239 underlay = basic_underlay()
240
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300241 else:
242 # 1. hardware environment created and powered on
243 # 2. config.underlay.ssh contains SSH access to provisioned nodes
244 # (can be passed from external config with TESTS_CONFIGS variable)
Dennis Dmitriev2a13a132016-11-04 00:56:23 +0200245 underlay = underlay_ssh_manager.UnderlaySSHManager(config)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300246
247 return underlay
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300248
249
Dennis Dmitriev986326a2018-02-12 17:24:26 +0200250@pytest.fixture(scope='function')
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200251def grab_versions(request, func_name, underlay):
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300252 """Fixture for grab package versions at the end of test
253
254 Marks:
255 grab_versions(name=None) - make snapshot if test is passed. If
256 name argument provided, it will be used for creating data,
257 otherwise, test function name will be used
258
259 """
260 grab_version = request.keywords.get('grab_versions', None)
261
262 def test_fin():
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200263 fixture_failed = (hasattr(request.node, 'rep_setup') and
264 request.node.rep_setup.failed)
265 test_passed = (hasattr(request.node, 'rep_call') and
266 request.node.rep_call.passed)
267 test_failed = (hasattr(request.node, 'rep_call') and
268 request.node.rep_call.failed)
269
270 if fixture_failed or test_passed or test_failed:
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300271 artifact_name = utils.extract_name_from_mark(grab_version) or \
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200272 "{}".format(func_name)
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300273 underlay.get_logs(artifact_name)
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200274
275 if grab_version:
276 request.addfinalizer(test_fin)