blob: db1d7a73271a190139861954e15089eb7312289f [file] [log] [blame]
Dmitriy Kruglovd8c76bc2019-09-26 09:30:38 +02001# Copyright 2019 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.
14import json
15import pytest
16
17from devops.helpers import helpers
18
19from tcp_tests import logger
20from tcp_tests import settings
21
22LOG = logger.logger
23
24
25class TestUbuntuSecurityUpdates(object):
26 """Test class for verification of obtaining Ubuntu security updates"""
27
28 ENV_NAME = settings.ENV_NAME
29 UPGRADE_CMD = (
30 'export DEBIAN_FRONTEND=noninteractive && '
31 'apt-get update && '
32 'apt-get -y upgrade && '
33 'apt-get -y -o Dpkg::Options::="--force-confdef" '
34 ' -o Dpkg::Options::="--force-confnew" dist-upgrade'
35 )
36 INST_LINUX_HEADERS_CMD = (
37 "export DEBIAN_FRONTEND=noninteractive && "
38 "apt-get -y install linux-headers-generic"
39 )
40
41 UPDATE_JOB_NAME = "deploy-update-package"
42 UPDATE_JOB_PARAMETERS = {
43 "ASK_CONFIRMATION": False,
44 "TARGET_SERVERS": ''
45 }
46
47 SANITY_JOB_NAME = 'cvp-sanity'
48 SANITY_JOB_PARAMETERS = {
49 'EXTRA_PARAMS': {
50 'envs': ["tests_set=-k 'not test_ceph_health'"]
51 }
52 }
53
54 JENKINS_START_TIMEOUT = 60
55
56 def get_available_pkg_updates(self, nodes, salt):
57 """Collect available package updates for given nodes
58
59 :param nodes: list, nodes to collect available updates for
60 :param salt: SaltManager, tcp-qa Salt manager instance
61 :return: dict, update candidates for nodes
62 """
63 updates = {}
64 for node in nodes:
65 updates[node] = salt.local(
66 node, "pkg.list_upgrades")['return'][0][node]
67 return updates
68
69 def run_cvp_sanity(self, dt):
70 """A wrapper for executing cvp-sanity pipeline
71
72 :param dt: DrivetrainManager, tcp-qa Drivetrain manager instance
73 :return: str, build execution status of cvp-sanity pipeline
74 """
75 return dt.start_job_on_cid_jenkins(
76 job_name=self.SANITY_JOB_NAME,
77 job_parameters=self.SANITY_JOB_PARAMETERS,
78 start_timeout=self.JENKINS_START_TIMEOUT,
79 build_timeout=60 * 15
80 )
81
82 def reboot_hw_node(self, ssh, salt, node):
83 """Reboot the given node and wait for it to start back
84
85 :param ssh: UnderlaySSHManager, tcp-qa SSH manager instance
86 :param salt: SaltManager, tcp-qa Salt manager instance
87 :param node: str, name of the node to reboot
88 """
89 LOG.info("Sending reboot command to '{}' node.".format(node))
90 remote = ssh.remote(node_name=node)
91 remote.execute_async("/sbin/shutdown -r now")
92
93 # Wait for restarted node to boot and become accessible
94 helpers.wait_pass(
95 lambda: salt.local(node, "test.ping", timeout=5),
96 timeout=60 * 10, interval=5)
97
98 # TODO: finish the test once ASK_CONFIRMATION option is added to
99 # 'deploy-update-package' pipeline
100 @pytest.mark.grab_versions
101 @pytest.mark.ubuntu_security_updates_pipeline
102 def _test_obtaining_ubuntu_security_updates_via_pipeline(
103 self, salt_actions, drivetrain_actions, show_step):
104 """Test obtaining Ubuntu security updates using Jenkins
105
106 Scenario:
107 1. Collect available package upgrades for nodes of the given server
108 role
109 2. Execute deploy-update-package pipeline for the given server role
110 3. Collect available package upgrades for server role nodes again
111 4. Check that there is no candidates for upgrade
112 5. Run cvp-sanity tests
113
114 Duration: ~ min
115 """
116 salt = salt_actions
117 dt = drivetrain_actions
118
119 role = "mon*"
120 nodes = salt.local(role, "test.ping")['return'][0].keys()
121
122 # Collect available package upgrades for nodes
123 show_step(1)
124 updates = self.get_available_pkg_updates(nodes, salt)
125 LOG.info("Packages to be updated on nodes:\n{}".format(
126 json.dumps(updates, indent=4)))
127
128 # Execute 'deploy-update-package' pipeline to upgrade packages on nodes
129 show_step(2)
130 self.UPDATE_JOB_PARAMETERS["TARGET_SERVERS"] = role
131 status = dt.start_job_on_cid_jenkins(
132 job_name=self.UPDATE_JOB_NAME,
133 job_parameters=self.UPDATE_JOB_PARAMETERS,
134 start_timeout=self.JENKINS_START_TIMEOUT,
135 build_timeout=60 * 15
136 )
137 assert status == 'SUCCESS', (
138 "'{}' job run status is {} after upgrading packages on {} nodes. "
139 "Please check the build and executed stages.".format(
140 self.UPDATE_JOB_NAME, status, role)
141 )
142
143 # Collect available package upgrades for nodes again
144 show_step(3)
145 post_upgrade = self.get_available_pkg_updates(nodes, salt)
146
147 # Check that there is no available package upgrades
148 show_step(4)
149 for node in nodes:
150 assert not post_upgrade[node], (
151 "{} node still has upgrade candidates. Please check the "
152 "following packages and the reason why they are not "
153 "updated:\n{}".format(node, post_upgrade[node])
154 )
155
156 # Execute cvp-sanity tests
157 show_step(5)
158 status = self.run_cvp_sanity(dt)
159 assert status == 'SUCCESS', (
160 "'{0}' job run status is {1} after executing CVP-Sanity "
161 "tests".format(
162 self.SANITY_JOB_NAME, status)
163 )
164
165 @pytest.mark.grab_versions
166 @pytest.mark.ubuntu_security_updates_manual_infra_vms
167 def test_obtaining_ubuntu_security_updates_manual_infra_vms(
168 self, salt_actions, drivetrain_actions, show_step):
169 """Test obtaining Ubuntu security updates on virtual infra nodes.
170 Repeat the scenario for 01, 02 and 03 indexes of nodes.
171
172 Scenario:
173 1. Select set of virtual nodes for upgrade
174 2. Collect available package upgrades for the nodes
175 3. Upgrade the nodes
176 4. Collect available package upgrades for the nodes again
177 5. Check that there is no candidates for upgrade on the nodes
178 6. Run cvp-sanity tests
179
180 Duration: ~ 100 min
181 """
182 salt = salt_actions
183 dt = drivetrain_actions
184
185 for index in ('01', '02', '03'):
186 msg = ("# Executing scenario for '{i}' index of nodes #".format(
187 i=index))
188 LOG.info(
189 "\n\n{pad}\n{msg}\n{pad}".format(pad="#" * len(msg), msg=msg))
190
191 # Select set of nodes for current iteration of updates
192 show_step(1)
193 tgt = "*{}* and E@^(?!kvm|cfg|cmp|osd).*$".format(index)
194 nodes = salt.local(tgt, "test.ping")['return'][0].keys()
195 LOG.info("Nodes to be upgraded:\n{}".format(
196 json.dumps(nodes, indent=4)))
197
198 # Collect available package upgrades for the nodes
199 show_step(2)
200 updates = self.get_available_pkg_updates(nodes, salt)
201
202 # Upgrade the selected nodes
203 show_step(3)
204 for node in nodes:
205 LOG.info(
206 "Starting upgrade of '{}' node.\nThe following packages "
207 "will be updated:\n{}".format(
208 node, json.dumps(updates[node], indent=4))
209 )
210 salt.cmd_run(node, self.UPGRADE_CMD)
211
212 # Collect available package upgrades for the nodes again
213 show_step(4)
214 post_upgrade = self.get_available_pkg_updates(nodes, salt)
215
216 # Check that there is no package upgrades candidates on the nodes
217 show_step(5)
218 missed_upd = {
219 node: pkgs for (node, pkgs) in post_upgrade.items() if pkgs}
220 assert not missed_upd, (
221 "{} nodes still have upgrade candidates. Please check the "
222 "nodes and reason why the listed packages are not "
223 "updated:\n{}".format(
224 missed_upd.keys(), json.dumps(missed_upd, indent=4))
225 )
226
227 # Execute cvp-sanity tests
228 show_step(6)
229 status = self.run_cvp_sanity(dt)
230 assert status == 'SUCCESS', (
231 "'{0}' job run status is {1} after executing CVP-Sanity smoke "
232 "tests".format(self.SANITY_JOB_NAME, status))
233
234 @pytest.mark.grab_versions
235 @pytest.mark.ubuntu_security_updates_manual_hw_nodes
236 def test_obtaining_ubuntu_security_updates_manual_hw_nodes(
237 self,
238 salt_actions,
239 underlay_actions,
240 drivetrain_actions,
241 show_step):
242 """Test obtaining Ubuntu security updates on HW nodes.
243 Repeat the scenario for 01, 02 and 03 indexes of nodes.
244
245 Scenario:
246 1. Select set HW nodes for upgrade
247 2. Collect available package upgrades for the nodes
248 3. Upgrade the nodes
249 4. Collect available package upgrades for the nodes again
250 5. Check that there is no candidates for upgrade on the nodes
251 6. Run cvp-sanity tests
252
253 Duration: ~ 70 min
254 """
255 salt = salt_actions
256 ssh = underlay_actions
257 dt = drivetrain_actions
258
259 for index in ('01', '02', '03'):
260 msg = ("# Executing scenario for '{i}' index of nodes #".format(
261 i=index))
262 LOG.info(
263 "\n\n{pad}\n{msg}\n{pad}".format(pad="#" * len(msg), msg=msg))
264
265 # Select set of nodes for current iteration of updates
266 show_step(1)
267 tgt = "E@^(kvm|cmp).?{}.*$".format(index)
268 nodes = salt.local(tgt, "test.ping")['return'][0].keys()
269 LOG.info("Nodes to be upgraded:\n{}".format(
270 json.dumps(nodes, indent=4)))
271
272 # Collect available package upgrades for the nodes
273 show_step(2)
274 updates = self.get_available_pkg_updates(nodes, salt)
275
276 # Upgrade the selected nodes
277 show_step(3)
278 for node in nodes:
279 LOG.info(
280 "Starting upgrade of '{}' node.\nThe following packages "
281 "will be updated:\n{}".format(
282 node, json.dumps(updates[node], indent=4))
283 )
284 salt.cmd_run(node, self.UPGRADE_CMD)
285 # Update Linux headers on compute nodes
286 if "cmp" in node:
287 LOG.info(
288 "Updating linux headers on '{}' node.".format(node))
289 salt.cmd_run(node, self.INST_LINUX_HEADERS_CMD)
290
291 # Reboot the node after upgrade
292 LOG.info("Starting reboot of '{}' node.".format(node))
293 self.reboot_hw_node(ssh, salt, node)
294 LOG.info("'{}' node is back after reboot.".format(node))
295
296 # Collect available package upgrades for the nodes again
297 show_step(4)
298 post_upgrade = self.get_available_pkg_updates(nodes, salt)
299
300 # Check that there is no package upgrades candidates on the nodes
301 show_step(5)
302 missed_upd = {
303 node: pkgs for (node, pkgs) in post_upgrade.items() if pkgs}
304 assert not missed_upd, (
305 "{} nodes still have upgrade candidates. Please check the "
306 "nodes and reason why the listed packages are not "
307 "updated:\n{}".format(
308 missed_upd.keys(), json.dumps(missed_upd, indent=4))
309 )
310
311 # Execute cvp-sanity tests
312 show_step(6)
313 status = self.run_cvp_sanity(dt)
314 assert status == 'SUCCESS', (
315 "'{0}' job run status is {1} after executing CVP-Sanity "
316 "tests".format(self.SANITY_JOB_NAME, status))