blob: 9b4bed004f92e2e7f6c01c7c28b43048b352e4f8 [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
Dennis Dmitriev2a13a132016-11-04 00:56:23 +0200185 underlay = underlay_ssh_manager.UnderlaySSHManager(config)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300186
187 if not config.underlay.lvm:
188 underlay.enable_lvm(hardware.lvm_storages())
189 config.underlay.lvm = underlay.config_lvm
190
191 hardware.create_snapshot(ext.SNAPSHOT.underlay)
192
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200193 return underlay
194
195 def day1_underlay():
196 hardware.start(
197 underlay_node_roles=['salt_master'],
198 timeout=config.underlay.bootstrap_timeout)
199
200 config.underlay.ssh = hardware.get_ssh_data(
201 roles=config.underlay.roles)
202
203 underlay = underlay_ssh_manager.UnderlaySSHManager(config)
204
205 LOG.info("Generate MACs for MaaS")
206 macs = {
207 n.name.split('.')[0]: {
208 "interface": {
209 "mac": n.get_interface_by_network_name('admin').mac_address}, # noqa
210 "power_parameters": {
211 "power_address": "{}:{}".format(
212 n.get_interface_by_network_name('admin').l2_network_device.address_pool.get_ip('l2_network_device'), # noqa
213 n.bmc_port
214 )}} for n in hardware.slave_nodes}
215
216 config.day1_cfg_config.maas_machines_macs = {
217 "parameters": {
218 "maas": {
219 "region": {
220 "machines": macs}}}}
221
222 if not config.underlay.lvm:
223 underlay.enable_lvm(hardware.lvm_storages())
224 config.underlay.lvm = underlay.config_lvm
225
226 for node in hardware.slave_nodes:
227 # For correct comissioning by MaaS nodes should be powered off
228 node.destroy()
229
230 hardware.create_snapshot(ext.SNAPSHOT.underlay)
231
232 return underlay
233
234 if not config.underlay.ssh:
235 if request.node.get_marker('day1_underlay'):
236 underlay = day1_underlay()
237 else:
238 underlay = basic_underlay()
239
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300240 else:
241 # 1. hardware environment created and powered on
242 # 2. config.underlay.ssh contains SSH access to provisioned nodes
243 # (can be passed from external config with TESTS_CONFIGS variable)
Dennis Dmitriev2a13a132016-11-04 00:56:23 +0200244 underlay = underlay_ssh_manager.UnderlaySSHManager(config)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300245
246 return underlay
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300247
248
Dennis Dmitriev986326a2018-02-12 17:24:26 +0200249@pytest.fixture(scope='function')
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200250def grab_versions(request, func_name, underlay):
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300251 """Fixture for grab package versions at the end of test
252
253 Marks:
254 grab_versions(name=None) - make snapshot if test is passed. If
255 name argument provided, it will be used for creating data,
256 otherwise, test function name will be used
257
258 """
259 grab_version = request.keywords.get('grab_versions', None)
260
261 def test_fin():
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200262 fixture_failed = (hasattr(request.node, 'rep_setup') and
263 request.node.rep_setup.failed)
264 test_passed = (hasattr(request.node, 'rep_call') and
265 request.node.rep_call.passed)
266 test_failed = (hasattr(request.node, 'rep_call') and
267 request.node.rep_call.failed)
268
269 if fixture_failed or test_passed or test_failed:
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300270 artifact_name = utils.extract_name_from_mark(grab_version) or \
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200271 "{}".format(func_name)
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300272 underlay.get_logs(artifact_name)
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200273
274 if grab_version:
275 request.addfinalizer(test_fin)