blob: a3bcea4e816a914e70b187eeb4ac9753bf369abf [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 Dmitrievb3b37492018-07-08 21:23:00 +0300153@pytest.fixture(scope="function")
154def underlay_actions(config):
155 """Fixture that provides SSH access to underlay objects.
156
157 :param config: oslo_config object that keeps various parameters
158 across the fixtures, tests and test runs.
159 All SSH data is taken from the provided config.
160 :rtype UnderlaySSHManager: Object that encapsulate SSH credentials;
161 - provide list of underlay nodes;
162 - provide SSH access to underlay nodes using
163 node names or node IPs.
164 """
165 return underlay_ssh_manager.UnderlaySSHManager(config)
166
167
Dennis Dmitriev535869c2016-11-16 22:38:06 +0200168@pytest.mark.revert_snapshot(ext.SNAPSHOT.underlay)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300169@pytest.fixture(scope="function")
Dennis Dmitrievea214632018-09-19 16:30:52 +0300170def underlay(request, revert_snapshot, config, hardware, underlay_actions):
Dennis Dmitrievb3b37492018-07-08 21:23:00 +0300171 """Fixture that bootstraps the environment underlay.
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300172
173 - Starts the 'hardware' environment and creates 'underlay' with required
174 configuration.
175 - Fills the following object using the 'hardware' fixture:
176 config.underlay.ssh = JSONList of SSH access credentials for nodes.
177 This list will be used for initialization the
178 model UnderlaySSHManager, see it for details.
179
180 :rtype UnderlaySSHManager: Object that encapsulate SSH credentials;
181 - provide list of underlay nodes;
182 - provide SSH access to underlay nodes using
183 node names or node IPs.
184 """
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300185 # Create Underlay
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200186
187 def basic_underlay():
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300188 # If config.underlay.ssh wasn't provided from external config, then
189 # try to get necessary data from hardware manager (fuel-devops)
Dennis Dmitriev7b9538f2017-05-15 17:01:34 +0300190
191 # for devops manager: power on nodes and wait for SSH
192 # for empty manager: do nothing
193 # for maas manager: provision nodes and wait for SSH
194 hardware.start(underlay_node_roles=config.underlay.roles,
195 timeout=config.underlay.bootstrap_timeout)
196
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300197 config.underlay.ssh = hardware.get_ssh_data(
198 roles=config.underlay.roles)
199
Dmitry Tyzhnenko34595f82018-06-12 19:03:12 +0300200 LOG.info("Config - {}".format(config))
Dennis Dmitrievea214632018-09-19 16:30:52 +0300201 underlay_actions.add_config_ssh(config.underlay.ssh)
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300202
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300203 hardware.create_snapshot(ext.SNAPSHOT.underlay)
204
Dennis Dmitrievea214632018-09-19 16:30:52 +0300205 return underlay_actions
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200206
207 def day1_underlay():
208 hardware.start(
209 underlay_node_roles=['salt_master'],
210 timeout=config.underlay.bootstrap_timeout)
211
212 config.underlay.ssh = hardware.get_ssh_data(
213 roles=config.underlay.roles)
Dennis Dmitrievea214632018-09-19 16:30:52 +0300214 underlay_actions.add_config_ssh(config.underlay.ssh)
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200215
216 LOG.info("Generate MACs for MaaS")
217 macs = {
218 n.name.split('.')[0]: {
219 "interface": {
220 "mac": n.get_interface_by_network_name('admin').mac_address}, # noqa
221 "power_parameters": {
222 "power_address": "{}:{}".format(
223 n.get_interface_by_network_name('admin').l2_network_device.address_pool.get_ip('l2_network_device'), # noqa
224 n.bmc_port
225 )}} for n in hardware.slave_nodes}
226
227 config.day1_cfg_config.maas_machines_macs = {
228 "parameters": {
229 "maas": {
230 "region": {
231 "machines": macs}}}}
232
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200233 for node in hardware.slave_nodes:
234 # For correct comissioning by MaaS nodes should be powered off
235 node.destroy()
236
237 hardware.create_snapshot(ext.SNAPSHOT.underlay)
238
Dennis Dmitrievea214632018-09-19 16:30:52 +0300239 return underlay_actions
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200240
241 if not config.underlay.ssh:
242 if request.node.get_marker('day1_underlay'):
Dennis Dmitrievea214632018-09-19 16:30:52 +0300243 return day1_underlay()
Dmitry Tyzhnenko35413c02018-03-05 14:12:37 +0200244 else:
Dennis Dmitrievea214632018-09-19 16:30:52 +0300245 return basic_underlay()
Dennis Dmitriev6f59add2016-10-18 13:45:27 +0300246 else:
247 # 1. hardware environment created and powered on
248 # 2. config.underlay.ssh contains SSH access to provisioned nodes
249 # (can be passed from external config with TESTS_CONFIGS variable)
Dennis Dmitrievea214632018-09-19 16:30:52 +0300250 return underlay_actions
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300251
252
Dennis Dmitriev986326a2018-02-12 17:24:26 +0200253@pytest.fixture(scope='function')
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200254def grab_versions(request, func_name, underlay):
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300255 """Fixture for grab package versions at the end of test
256
257 Marks:
258 grab_versions(name=None) - make snapshot if test is passed. If
259 name argument provided, it will be used for creating data,
260 otherwise, test function name will be used
261
262 """
263 grab_version = request.keywords.get('grab_versions', None)
264
265 def test_fin():
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200266 fixture_failed = (hasattr(request.node, 'rep_setup') and
267 request.node.rep_setup.failed)
268 test_passed = (hasattr(request.node, 'rep_call') and
269 request.node.rep_call.passed)
270 test_failed = (hasattr(request.node, 'rep_call') and
271 request.node.rep_call.failed)
272
273 if fixture_failed or test_passed or test_failed:
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300274 artifact_name = utils.extract_name_from_mark(grab_version) or \
Dennis Dmitriev2d643bc2017-12-04 12:23:47 +0200275 "{}".format(func_name)
Tatyana Leontovichab47e162017-10-06 16:53:30 +0300276 underlay.get_logs(artifact_name)
Dennis Dmitrievf0b2afe2018-02-28 13:25:02 +0200277
278 if grab_version:
279 request.addfinalizer(test_fin)