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