From bbc14f9e9d16c362fdffee7382119faf9740ef16 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 01/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file adjusting existing upgrade test to be able to run on canonical setup by removing redundant imports --- tests/platform_tests/test_secure_boot.py | 48 ++++++++++++++++++++++++ tests/upgrade_path/test_upgrade_path.py | 24 ++++-------- 2 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 tests/platform_tests/test_secure_boot.py diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py new file mode 100644 index 0000000000..aca49b224c --- /dev/null +++ b/tests/platform_tests/test_secure_boot.py @@ -0,0 +1,48 @@ +""" +this tests checks secure boot upgrade +""" +import logging +import pytest +from tests.common.errors import RunAnsibleModuleFail +from tests.common import reboot +from tests.common.helpers.assertions import pytest_assert +from tests.upgrade_path.upgrade_helpers import check_services, check_sonic_version, \ + install_sonic, check_reboot_cause +from ansible.utils.unsafe_proxy import AnsibleUnsafeText +from tests.upgrade_path.test_upgrade_path import upgrade_path_lists + +pytestmark = [ + pytest.mark.topology('any'), + pytest.mark.device_type('physical'), + pytest.mark.disable_loganalyzer, +] + +logger = logging.getLogger(__name__) + + +def test_non_secure_boot_upgrade_failure(duthosts, enum_rand_one_per_hwsku_hostname, localhost, upgrade_path_lists, tbinfo, capsys): + """ + @summary: This test case validates non successful upgrade of a given non secure image + """ + duthost = duthosts[enum_rand_one_per_hwsku_hostname] + upgrade_type, _, non_secure_img, _ = upgrade_path_lists + logger.info("get current version installed") + current_version = duthost.image_facts()['ansible_facts']['ansible_image_facts']['current'] + # install non secure image + logger.info("install non secure image - expect fail, image url = {}".format(non_secure_img)) + result = "image install failure" # because we expect fail + try: + # in case of success result will return target image name + result = install_sonic(duthost, non_secure_img, tbinfo) + except RunAnsibleModuleFail as err: + err_msg = str(err.results._check_key("module_stdout")) + logger.info("Expected fail, msg : {}".format(err_msg)) + pytest_assert("Failure: CMS signature verification failed" in str(err_msg), "failure was not due to security limitations") + finally: + pytest_assert(result=="image install failure", "install non secure image should not succeed") + logger.info("Cold reboot the DUT") + reboot(duthost, localhost) + logger.info("Check version has not changed after reboot") + check_sonic_version(duthost, current_version) + check_reboot_cause(duthost, upgrade_type) + check_services(duthost) \ No newline at end of file diff --git a/tests/upgrade_path/test_upgrade_path.py b/tests/upgrade_path/test_upgrade_path.py index e9d3374fc9..d231388478 100644 --- a/tests/upgrade_path/test_upgrade_path.py +++ b/tests/upgrade_path/test_upgrade_path.py @@ -5,16 +5,8 @@ from tests.common.reboot import get_reboot_cause from tests.common.reboot import REBOOT_TYPE_COLD from tests.upgrade_path.upgrade_helpers import check_services, install_sonic, check_sonic_version, get_reboot_command -from tests.upgrade_path.upgrade_helpers import restore_image from tests.common.fixtures.advanced_reboot import get_advanced_reboot -from tests.platform_tests.verify_dut_health import verify_dut_health -from tests.common.fixtures.duthost_utils import backup_and_restore_config_db - -from tests.platform_tests.conftest import advanceboot_loganalyzer, advanceboot_neighbor_restore # lgtm[py/unused-import] -from tests.common.fixtures.ptfhost_utils import copy_ptftests_directory # lgtm[py/unused-import] -from tests.common.fixtures.ptfhost_utils import change_mac_addresses # lgtm[py/unused-import] -from tests.common.fixtures.ptfhost_utils import remove_ip_addresses # lgtm[py/unused-import] -from tests.common.fixtures.ptfhost_utils import copy_arp_responder_py # lgtm[py/unused-import] +from tests.platform_tests.conftest import advanceboot_loganalyzer # lgtm[py/unused-import] from tests.platform_tests.warmboot_sad_cases import get_sad_case_list, SAD_CASE_LIST @@ -22,8 +14,7 @@ pytestmark = [ pytest.mark.topology('any'), pytest.mark.sanity_check(skip_sanity=True), - pytest.mark.disable_loganalyzer, - pytest.mark.skip_check_dut_health + pytest.mark.disable_loganalyzer ] logger = logging.getLogger(__name__) @@ -44,8 +35,8 @@ def upgrade_path_lists(request): @pytest.mark.device_type('vs') -def test_upgrade_path(localhost, duthosts, ptfhost, rand_one_dut_hostname, nbrhosts, fanouthosts, tbinfo, - restore_image, get_advanced_reboot, verify_dut_health, advanceboot_loganalyzer, +def test_upgrade_path(localhost, duthosts, rand_one_dut_hostname, tbinfo, + get_advanced_reboot, advanceboot_loganalyzer, upgrade_path_lists): duthost = duthosts[rand_one_dut_hostname] upgrade_type, from_list_images, to_list_images, _ = upgrade_path_lists @@ -80,10 +71,9 @@ def test_upgrade_path(localhost, duthosts, ptfhost, rand_one_dut_hostname, nbrho @pytest.mark.device_type('vs') -def test_warm_upgrade_sad_path(localhost, duthosts, ptfhost, rand_one_dut_hostname, nbrhosts, fanouthosts, vmhost, tbinfo, - restore_image, get_advanced_reboot, verify_dut_health, advanceboot_loganalyzer, - upgrade_path_lists, backup_and_restore_config_db, advanceboot_neighbor_restore, - sad_case_type): +def test_warm_upgrade_sad_path(localhost, duthosts, rand_one_dut_hostname, nbrhosts, fanouthosts, vmhost, tbinfo, + get_advanced_reboot, advanceboot_loganalyzer, + upgrade_path_lists, sad_case_type): duthost = duthosts[rand_one_dut_hostname] upgrade_type, from_list_images, to_list_images, _ = upgrade_path_lists from_list = from_list_images.split(',') From 77de6c270cd2f371b779d640dc03f3434f552bc0 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 02/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file adjusting existing upgrade test to be able to run on canonical setup by removing redundant imports --- tests/platform_tests/test_secure_boot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index aca49b224c..1ead72682c 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -8,13 +8,13 @@ from tests.common.helpers.assertions import pytest_assert from tests.upgrade_path.upgrade_helpers import check_services, check_sonic_version, \ install_sonic, check_reboot_cause -from ansible.utils.unsafe_proxy import AnsibleUnsafeText from tests.upgrade_path.test_upgrade_path import upgrade_path_lists pytestmark = [ pytest.mark.topology('any'), - pytest.mark.device_type('physical'), + pytest.mark.sanity_check(skip_sanity=True), pytest.mark.disable_loganalyzer, + pytest.mark.skip_check_dut_health ] logger = logging.getLogger(__name__) @@ -45,4 +45,4 @@ def test_non_secure_boot_upgrade_failure(duthosts, enum_rand_one_per_hwsku_hostn logger.info("Check version has not changed after reboot") check_sonic_version(duthost, current_version) check_reboot_cause(duthost, upgrade_type) - check_services(duthost) \ No newline at end of file + check_services(duthost) From 8ff103eb0c07f1bd51c79d2879412608f877955f Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 03/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file adjusting existing upgrade test to be able to run on canonical setup by removing redundant imports --- tests/upgrade_path/test_upgrade_path.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/upgrade_path/test_upgrade_path.py b/tests/upgrade_path/test_upgrade_path.py index d231388478..44581cf9aa 100644 --- a/tests/upgrade_path/test_upgrade_path.py +++ b/tests/upgrade_path/test_upgrade_path.py @@ -14,7 +14,8 @@ pytestmark = [ pytest.mark.topology('any'), pytest.mark.sanity_check(skip_sanity=True), - pytest.mark.disable_loganalyzer + pytest.mark.disable_loganalyzer, + pytest.mark.skip_check_dut_health ] logger = logging.getLogger(__name__) From 460a5ca185bb9c11f3a5b0732e24df01e44a0d00 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 04/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file adjusting existing upgrade test to be able to run on canonical setup by removing redundant imports --- tests/platform_tests/test_secure_boot.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index 1ead72682c..16680817d3 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -1,5 +1,12 @@ """ this tests checks secure boot upgrade + +In order to run this test, you need to specify the following argument: + 1. --target_image_list (to contain the non secure image path) + 2. --upgrade_type always set to cold +e.g.: +(from tests dir) + pytest platform_tests/test_secure_boot.py --target_image_list non_secure_image.bin --upgrade_type cold """ import logging import pytest From a0b570c9d3a4f773e07c77bf4cab8a823c2bf771 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 05/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file adjusting existing upgrade test to be able to run on canonical setup by removing redundant imports --- tests/platform_tests/test_secure_boot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index 16680817d3..cd6a335496 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -3,7 +3,7 @@ In order to run this test, you need to specify the following argument: 1. --target_image_list (to contain the non secure image path) - 2. --upgrade_type always set to cold + 2. --upgrade_type always set always to cold e.g.: (from tests dir) pytest platform_tests/test_secure_boot.py --target_image_list non_secure_image.bin --upgrade_type cold From 33212e6c16cb3677ff7c3f1bbc5e1a40bc38f0ea Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 06/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file adjusting existing upgrade test to be able to run on canonical setup by removing redundant imports --- tests/platform_tests/test_secure_boot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index cd6a335496..c6c7895602 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -2,8 +2,8 @@ this tests checks secure boot upgrade In order to run this test, you need to specify the following argument: - 1. --target_image_list (to contain the non secure image path) - 2. --upgrade_type always set always to cold + 1. --target_image_list (to contain your non secure image path e.g. /tmp/images/my_non_secure_img.bin) + 2. --upgrade_type always set to cold (see run command for example) e.g.: (from tests dir) pytest platform_tests/test_secure_boot.py --target_image_list non_secure_image.bin --upgrade_type cold From 446fe7e9c2070eff164cae159475e7b67264e85f Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 07/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file adjusting existing upgrade test to be able to run on canonical setup by removing redundant imports --- tests/platform_tests/test_secure_boot.py | 28 ++++++++++++++++-------- tests/upgrade_path/test_upgrade_path.py | 21 +++++++++++++----- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index c6c7895602..17cc831b82 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -3,13 +3,13 @@ In order to run this test, you need to specify the following argument: 1. --target_image_list (to contain your non secure image path e.g. /tmp/images/my_non_secure_img.bin) - 2. --upgrade_type always set to cold (see run command for example) e.g.: (from tests dir) - pytest platform_tests/test_secure_boot.py --target_image_list non_secure_image.bin --upgrade_type cold + pytest platform_tests/test_secure_boot.py --target_image_list non_secure_image.bin """ import logging import pytest +import re from tests.common.errors import RunAnsibleModuleFail from tests.common import reboot from tests.common.helpers.assertions import pytest_assert @@ -27,14 +27,26 @@ logger = logging.getLogger(__name__) -def test_non_secure_boot_upgrade_failure(duthosts, enum_rand_one_per_hwsku_hostname, localhost, upgrade_path_lists, tbinfo, capsys): +def get_current_version(duthost): + ''' + @summary: extract the current version installed as shown in the "show boot" output. + :param duthost: device under test + :return: the version currently installed + ''' + output = duthost.shell("show boot")['stdout'] + results = re.findall("Current\s*\:\s*(.*)\n", output) + pytest_assert(len(results) > 0, "Current image is empty!") + return results[0] + + +def test_non_secure_boot_upgrade_failure(duthosts, enum_rand_one_per_hwsku_hostname, upgrade_path_lists, tbinfo): """ @summary: This test case validates non successful upgrade of a given non secure image """ duthost = duthosts[enum_rand_one_per_hwsku_hostname] upgrade_type, _, non_secure_img, _ = upgrade_path_lists - logger.info("get current version installed") - current_version = duthost.image_facts()['ansible_facts']['ansible_image_facts']['current'] + current_version = get_current_version(duthost) + logger.info("current version installed is {}".format(current_version)) # install non secure image logger.info("install non secure image - expect fail, image url = {}".format(non_secure_img)) result = "image install failure" # because we expect fail @@ -46,10 +58,8 @@ def test_non_secure_boot_upgrade_failure(duthosts, enum_rand_one_per_hwsku_hostn logger.info("Expected fail, msg : {}".format(err_msg)) pytest_assert("Failure: CMS signature verification failed" in str(err_msg), "failure was not due to security limitations") finally: + logger.info("reset the image installed back to original image - {}".format(current_version)) + duthost.shell("sonic-installer set-default {}",format(current_version)) pytest_assert(result=="image install failure", "install non secure image should not succeed") - logger.info("Cold reboot the DUT") - reboot(duthost, localhost) logger.info("Check version has not changed after reboot") check_sonic_version(duthost, current_version) - check_reboot_cause(duthost, upgrade_type) - check_services(duthost) diff --git a/tests/upgrade_path/test_upgrade_path.py b/tests/upgrade_path/test_upgrade_path.py index 44581cf9aa..e9d3374fc9 100644 --- a/tests/upgrade_path/test_upgrade_path.py +++ b/tests/upgrade_path/test_upgrade_path.py @@ -5,8 +5,16 @@ from tests.common.reboot import get_reboot_cause from tests.common.reboot import REBOOT_TYPE_COLD from tests.upgrade_path.upgrade_helpers import check_services, install_sonic, check_sonic_version, get_reboot_command +from tests.upgrade_path.upgrade_helpers import restore_image from tests.common.fixtures.advanced_reboot import get_advanced_reboot -from tests.platform_tests.conftest import advanceboot_loganalyzer # lgtm[py/unused-import] +from tests.platform_tests.verify_dut_health import verify_dut_health +from tests.common.fixtures.duthost_utils import backup_and_restore_config_db + +from tests.platform_tests.conftest import advanceboot_loganalyzer, advanceboot_neighbor_restore # lgtm[py/unused-import] +from tests.common.fixtures.ptfhost_utils import copy_ptftests_directory # lgtm[py/unused-import] +from tests.common.fixtures.ptfhost_utils import change_mac_addresses # lgtm[py/unused-import] +from tests.common.fixtures.ptfhost_utils import remove_ip_addresses # lgtm[py/unused-import] +from tests.common.fixtures.ptfhost_utils import copy_arp_responder_py # lgtm[py/unused-import] from tests.platform_tests.warmboot_sad_cases import get_sad_case_list, SAD_CASE_LIST @@ -36,8 +44,8 @@ def upgrade_path_lists(request): @pytest.mark.device_type('vs') -def test_upgrade_path(localhost, duthosts, rand_one_dut_hostname, tbinfo, - get_advanced_reboot, advanceboot_loganalyzer, +def test_upgrade_path(localhost, duthosts, ptfhost, rand_one_dut_hostname, nbrhosts, fanouthosts, tbinfo, + restore_image, get_advanced_reboot, verify_dut_health, advanceboot_loganalyzer, upgrade_path_lists): duthost = duthosts[rand_one_dut_hostname] upgrade_type, from_list_images, to_list_images, _ = upgrade_path_lists @@ -72,9 +80,10 @@ def test_upgrade_path(localhost, duthosts, rand_one_dut_hostname, tbinfo, @pytest.mark.device_type('vs') -def test_warm_upgrade_sad_path(localhost, duthosts, rand_one_dut_hostname, nbrhosts, fanouthosts, vmhost, tbinfo, - get_advanced_reboot, advanceboot_loganalyzer, - upgrade_path_lists, sad_case_type): +def test_warm_upgrade_sad_path(localhost, duthosts, ptfhost, rand_one_dut_hostname, nbrhosts, fanouthosts, vmhost, tbinfo, + restore_image, get_advanced_reboot, verify_dut_health, advanceboot_loganalyzer, + upgrade_path_lists, backup_and_restore_config_db, advanceboot_neighbor_restore, + sad_case_type): duthost = duthosts[rand_one_dut_hostname] upgrade_type, from_list_images, to_list_images, _ = upgrade_path_lists from_list = from_list_images.split(',') From 36d40dcc0184816c49e168e9e23acbd9fe6a747d Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 08/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 41 +++++++++++++----------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index 17cc831b82..3dfc9ae1f3 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -11,10 +11,8 @@ import pytest import re from tests.common.errors import RunAnsibleModuleFail -from tests.common import reboot from tests.common.helpers.assertions import pytest_assert -from tests.upgrade_path.upgrade_helpers import check_services, check_sonic_version, \ - install_sonic, check_reboot_cause +from tests.upgrade_path.upgrade_helpers import install_sonic from tests.upgrade_path.test_upgrade_path import upgrade_path_lists pytestmark = [ @@ -27,39 +25,46 @@ logger = logging.getLogger(__name__) -def get_current_version(duthost): +@pytest.fixture(scope='session', autouse=True) +def keep_same_version_installed(duthost): ''' @summary: extract the current version installed as shown in the "show boot" output. + and restore original image installed after the test run :param duthost: device under test :return: the version currently installed ''' output = duthost.shell("show boot")['stdout'] results = re.findall("Current\s*\:\s*(.*)\n", output) pytest_assert(len(results) > 0, "Current image is empty!") - return results[0] + current_version = results[0] + yield + duthost.shell("sonic-installer set-default {}", format(current_version)) -def test_non_secure_boot_upgrade_failure(duthosts, enum_rand_one_per_hwsku_hostname, upgrade_path_lists, tbinfo): +@pytest.fixture(scope='session') +def non_secure_image_path(upgrade_path_lists): + ''' + @summary: will extract the non secure image path from --target_image_list parameter + :return: given non secure image path + ''' + _, _, non_secure_img_path, _ = upgrade_path_lists + pytest_assert(len(non_secure_img_path) == 1, "Please specify one non-secure image path") + return non_secure_img_path + + +def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo): """ @summary: This test case validates non successful upgrade of a given non secure image """ - duthost = duthosts[enum_rand_one_per_hwsku_hostname] - upgrade_type, _, non_secure_img, _ = upgrade_path_lists - current_version = get_current_version(duthost) - logger.info("current version installed is {}".format(current_version)) # install non secure image - logger.info("install non secure image - expect fail, image url = {}".format(non_secure_img)) + logger.info("install non secure image - expect fail, image path = {}".format(non_secure_image_path)) result = "image install failure" # because we expect fail try: - # in case of success result will return target image name - result = install_sonic(duthost, non_secure_img, tbinfo) + # in case of success result will take the target image name + result = install_sonic(duthost, non_secure_image_path, tbinfo) except RunAnsibleModuleFail as err: err_msg = str(err.results._check_key("module_stdout")) logger.info("Expected fail, msg : {}".format(err_msg)) pytest_assert("Failure: CMS signature verification failed" in str(err_msg), "failure was not due to security limitations") finally: - logger.info("reset the image installed back to original image - {}".format(current_version)) - duthost.shell("sonic-installer set-default {}",format(current_version)) - pytest_assert(result=="image install failure", "install non secure image should not succeed") - logger.info("Check version has not changed after reboot") - check_sonic_version(duthost, current_version) + pytest_assert(result == "image install failure", "non-secure image was successfully installed") From 025449d5212b4f2a03ea88f23d52610954787562 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 09/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index 3dfc9ae1f3..ba00563ea3 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -1,11 +1,9 @@ """ this tests checks secure boot upgrade - In order to run this test, you need to specify the following argument: - 1. --target_image_list (to contain your non secure image path e.g. /tmp/images/my_non_secure_img.bin) -e.g.: -(from tests dir) - pytest platform_tests/test_secure_boot.py --target_image_list non_secure_image.bin + 1. --target_image_list (to contain one non-secure image path e.g. /tmp/images/my_non_secure_img.bin) +e.g.: (run from tests dir) + pytest platform_tests/test_secure_boot.py --target_image_list non_secure_image.bin """ import logging import pytest @@ -13,7 +11,6 @@ from tests.common.errors import RunAnsibleModuleFail from tests.common.helpers.assertions import pytest_assert from tests.upgrade_path.upgrade_helpers import install_sonic -from tests.upgrade_path.test_upgrade_path import upgrade_path_lists pytestmark = [ pytest.mark.topology('any'), @@ -24,7 +21,6 @@ logger = logging.getLogger(__name__) - @pytest.fixture(scope='session', autouse=True) def keep_same_version_installed(duthost): ''' From 28b2744aedcf83b9d87efea378fa46202f9d802e Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 10/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index ba00563ea3..ea5a761ca4 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -61,6 +61,7 @@ def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo) except RunAnsibleModuleFail as err: err_msg = str(err.results._check_key("module_stdout")) logger.info("Expected fail, msg : {}".format(err_msg)) - pytest_assert("Failure: CMS signature verification failed" in str(err_msg), "failure was not due to security limitations") + pytest_assert("Failure: CMS signature verification failed" in str(err_msg), + "failure was not due to security limitations") finally: - pytest_assert(result == "image install failure", "non-secure image was successfully installed") + pytest_assert(result == "image install failure", "non-secure image was successfully installed") \ No newline at end of file From f4277a943dc66aae8387f6b3ff008b356d4ed40c Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 11/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index ea5a761ca4..cfe9f0a597 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -30,7 +30,7 @@ def keep_same_version_installed(duthost): :return: the version currently installed ''' output = duthost.shell("show boot")['stdout'] - results = re.findall("Current\s*\:\s*(.*)\n", output) + results = re.findall(r"Current\s*\:\s*(.*)\n", output) pytest_assert(len(results) > 0, "Current image is empty!") current_version = results[0] yield @@ -54,7 +54,7 @@ def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo) """ # install non secure image logger.info("install non secure image - expect fail, image path = {}".format(non_secure_image_path)) - result = "image install failure" # because we expect fail + result = "image install failure" # because we expect fail try: # in case of success result will take the target image name result = install_sonic(duthost, non_secure_image_path, tbinfo) From 360db655bd1411c9164635bda0531241dbc68f5a Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 12/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index cfe9f0a597..2d38717be6 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -21,6 +21,7 @@ logger = logging.getLogger(__name__) + @pytest.fixture(scope='session', autouse=True) def keep_same_version_installed(duthost): ''' From fff9a52e4560d9f94174325520ddf7901255fc9b Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 13/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index 2d38717be6..f276f61958 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -63,6 +63,6 @@ def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo) err_msg = str(err.results._check_key("module_stdout")) logger.info("Expected fail, msg : {}".format(err_msg)) pytest_assert("Failure: CMS signature verification failed" in str(err_msg), - "failure was not due to security limitations") + "failure was not due to security limitations") finally: pytest_assert(result == "image install failure", "non-secure image was successfully installed") \ No newline at end of file From 5ed43f2af8d3247112464713774cb6ae365af258 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 14/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index f276f61958..6f06bd54f6 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -62,7 +62,8 @@ def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo) except RunAnsibleModuleFail as err: err_msg = str(err.results._check_key("module_stdout")) logger.info("Expected fail, msg : {}".format(err_msg)) - pytest_assert("Failure: CMS signature verification failed" in str(err_msg), - "failure was not due to security limitations") + pytest_assert( + "Failure: CMS signature verification failed" in str(err_msg), + "failure was not due to security limitations") finally: - pytest_assert(result == "image install failure", "non-secure image was successfully installed") \ No newline at end of file + pytest_assert(result == "image install failure", "non-secure image was successfully installed") From 4e52d116bf7fb175d8786f56027fead708749312 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 15/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index 6f06bd54f6..4a89ceb906 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -8,6 +8,7 @@ import logging import pytest import re +from tests.upgrade_path.test_upgrade_path import upgrade_path_lists from tests.common.errors import RunAnsibleModuleFail from tests.common.helpers.assertions import pytest_assert from tests.upgrade_path.upgrade_helpers import install_sonic @@ -35,7 +36,7 @@ def keep_same_version_installed(duthost): pytest_assert(len(results) > 0, "Current image is empty!") current_version = results[0] yield - duthost.shell("sonic-installer set-default {}", format(current_version)) + duthost.shell("sudo sonic-installer set-default {}", format(current_version)) @pytest.fixture(scope='session') From ed3b3a25b2eb1bf0a658b203bc578dedf917643c Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 16/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index 4a89ceb906..deb854c64a 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -23,7 +23,7 @@ logger = logging.getLogger(__name__) -@pytest.fixture(scope='session', autouse=True) +@pytest.fixture(scope='module', autouse=True) def keep_same_version_installed(duthost): ''' @summary: extract the current version installed as shown in the "show boot" output. @@ -40,13 +40,13 @@ def keep_same_version_installed(duthost): @pytest.fixture(scope='session') -def non_secure_image_path(upgrade_path_lists): +def non_secure_image_path(request): ''' @summary: will extract the non secure image path from --target_image_list parameter :return: given non secure image path ''' - _, _, non_secure_img_path, _ = upgrade_path_lists - pytest_assert(len(non_secure_img_path) == 1, "Please specify one non-secure image path") + non_secure_img_path = request.config.getoption('target_image_list') + logger.info("your non-secure image path is {}".format(non_secure_image_path)) return non_secure_img_path @@ -67,4 +67,4 @@ def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo) "Failure: CMS signature verification failed" in str(err_msg), "failure was not due to security limitations") finally: - pytest_assert(result == "image install failure", "non-secure image was successfully installed") + pytest_assert(result == "image install failure", "non-secure image was successfully installed") \ No newline at end of file From 98f05fda2f1a1bd091f83289a6dd2b1aea9020d7 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 17/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index deb854c64a..17f6d1b228 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -8,7 +8,6 @@ import logging import pytest import re -from tests.upgrade_path.test_upgrade_path import upgrade_path_lists from tests.common.errors import RunAnsibleModuleFail from tests.common.helpers.assertions import pytest_assert from tests.upgrade_path.upgrade_helpers import install_sonic @@ -46,8 +45,7 @@ def non_secure_image_path(request): :return: given non secure image path ''' non_secure_img_path = request.config.getoption('target_image_list') - logger.info("your non-secure image path is {}".format(non_secure_image_path)) - return non_secure_img_path + return str(non_secure_img_path) def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo): @@ -61,10 +59,11 @@ def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo) # in case of success result will take the target image name result = install_sonic(duthost, non_secure_image_path, tbinfo) except RunAnsibleModuleFail as err: - err_msg = str(err.results._check_key("module_stdout")) - logger.info("Expected fail, msg : {}".format(err_msg)) + output_msg = str(err.results._check_key("module_stdout")) + err_msg = str(err.results._check_key("msg")) + logger.info("Expected fail, err msg is : {}\n\noutput_msg is {}".format(err_msg, output_msg)) pytest_assert( - "Failure: CMS signature verification failed" in str(err_msg), + "Failure: CMS signature verification failed" in str(output_msg), "failure was not due to security limitations") finally: - pytest_assert(result == "image install failure", "non-secure image was successfully installed") \ No newline at end of file + pytest_assert(result == "image install failure", "non-secure image was successfully installed") From d8581432d69c78b901a9f27e728551b3b23fe749 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 18/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index 17f6d1b228..d412148891 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -28,7 +28,6 @@ def keep_same_version_installed(duthost): @summary: extract the current version installed as shown in the "show boot" output. and restore original image installed after the test run :param duthost: device under test - :return: the version currently installed ''' output = duthost.shell("show boot")['stdout'] results = re.findall(r"Current\s*\:\s*(.*)\n", output) From b255a46a0db8c9db12f7959063ac573cfcaee90e Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 19/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index d412148891..eee673cd0d 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -22,7 +22,7 @@ logger = logging.getLogger(__name__) -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope='function', autouse=True) def keep_same_version_installed(duthost): ''' @summary: extract the current version installed as shown in the "show boot" output. @@ -65,4 +65,4 @@ def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo) "Failure: CMS signature verification failed" in str(output_msg), "failure was not due to security limitations") finally: - pytest_assert(result == "image install failure", "non-secure image was successfully installed") + pytest_assert(result == "image install failure", "non-secure image was successfully installed") \ No newline at end of file From af387af592ef9da73caf04f47ecc46305fe40a01 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 20/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index eee673cd0d..60ff5b7b73 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -65,4 +65,5 @@ def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo) "Failure: CMS signature verification failed" in str(output_msg), "failure was not due to security limitations") finally: - pytest_assert(result == "image install failure", "non-secure image was successfully installed") \ No newline at end of file + pytest_assert(result == "image install failure", "non-secure image was successfully installed") + From 40218889dec066e2d9fac388db91bbea8a61ae76 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 21/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index 60ff5b7b73..f92a6674d0 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -66,4 +66,7 @@ def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo) "failure was not due to security limitations") finally: pytest_assert(result == "image install failure", "non-secure image was successfully installed") +<<<<<<< af387af592ef9da73caf04f47ecc46305fe40a01 +======= +>>>>>>> Adding new test case for non secure upgarde failure check: From 21309831e20175ca64be360f1846ec0660f61386 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 22/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_boot.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_boot.py index f92a6674d0..698a1e36ee 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_boot.py @@ -66,7 +66,3 @@ def test_non_secure_boot_upgrade_failure(duthost, non_secure_image_path, tbinfo) "failure was not due to security limitations") finally: pytest_assert(result == "image install failure", "non-secure image was successfully installed") -<<<<<<< af387af592ef9da73caf04f47ecc46305fe40a01 - -======= ->>>>>>> Adding new test case for non secure upgarde failure check: From dbb8c66e6960edcf1ef46fe77a234a98815bbe82 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 23/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- ...est_secure_boot.py => test_secure_upgrade.py} | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) rename tests/platform_tests/{test_secure_boot.py => test_secure_upgrade.py} (79%) diff --git a/tests/platform_tests/test_secure_boot.py b/tests/platform_tests/test_secure_upgrade.py similarity index 79% rename from tests/platform_tests/test_secure_boot.py rename to tests/platform_tests/test_secure_upgrade.py index 698a1e36ee..fdf8fe2306 100644 --- a/tests/platform_tests/test_secure_boot.py +++ b/tests/platform_tests/test_secure_upgrade.py @@ -1,9 +1,15 @@ """ -this tests checks secure boot upgrade +This tests checks secure upgrade feature. If we have a secure system with secured image installed +on it, the system is expected to install only secured images on it. So trying to install non-secure image +will cause fail and a print of failure message to console indicating it is not signed image. +This test case validates the error flow mentioned above. + In order to run this test, you need to specify the following argument: - 1. --target_image_list (to contain one non-secure image path e.g. /tmp/images/my_non_secure_img.bin) -e.g.: (run from tests dir) - pytest platform_tests/test_secure_boot.py --target_image_list non_secure_image.bin + + --target_image_list (to contain one non-secure image path e.g. /tmp/images/my_non_secure_img.bin) + +Example run from tests directory: + "pytest platform_tests/test_secure_boot.py --target_image_list non_secure_image.bin" """ import logging import pytest @@ -25,7 +31,7 @@ @pytest.fixture(scope='function', autouse=True) def keep_same_version_installed(duthost): ''' - @summary: extract the current version installed as shown in the "show boot" output. + @summary: extract the current version installed as shown in the "show boot" output and restore original image installed after the test run :param duthost: device under test ''' From 1f11b2adee9fe5ff8953c0464f1b8203530da450 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 24/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_upgrade.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/platform_tests/test_secure_upgrade.py b/tests/platform_tests/test_secure_upgrade.py index fdf8fe2306..d8f8cb0422 100644 --- a/tests/platform_tests/test_secure_upgrade.py +++ b/tests/platform_tests/test_secure_upgrade.py @@ -1,7 +1,7 @@ """ This tests checks secure upgrade feature. If we have a secure system with secured image installed on it, the system is expected to install only secured images on it. So trying to install non-secure image -will cause fail and a print of failure message to console indicating it is not signed image. +will cause fail and a print of failure message to console indicating it is not a secured image. This test case validates the error flow mentioned above. In order to run this test, you need to specify the following argument: @@ -9,7 +9,7 @@ --target_image_list (to contain one non-secure image path e.g. /tmp/images/my_non_secure_img.bin) Example run from tests directory: - "pytest platform_tests/test_secure_boot.py --target_image_list non_secure_image.bin" + "pytest platform_tests/test_secure_upgrade.py --target_image_list non_secure_image.bin" """ import logging import pytest From cb2435acb1794973000adf317711c97cb0872deb Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Wed, 9 Nov 2022 11:59:01 +0200 Subject: [PATCH 25/28] Adding new test case for non secure upgarde failure check: in this test case we validate non successful install of a given non secure image file --- tests/platform_tests/test_secure_upgrade.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/platform_tests/test_secure_upgrade.py b/tests/platform_tests/test_secure_upgrade.py index d8f8cb0422..12493d9d52 100644 --- a/tests/platform_tests/test_secure_upgrade.py +++ b/tests/platform_tests/test_secure_upgrade.py @@ -1,5 +1,5 @@ """ -This tests checks secure upgrade feature. If we have a secure system with secured image installed +This test checks secure upgrade feature. If we have a secure system with secured image installed on it, the system is expected to install only secured images on it. So trying to install non-secure image will cause fail and a print of failure message to console indicating it is not a secured image. This test case validates the error flow mentioned above. From 5e9520d7020cd7a9ba534d5c1064b90bd761569f Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Mon, 21 Nov 2022 15:23:26 +0200 Subject: [PATCH 26/28] Introducing new test case for default password change after initial reboot based on the california law. --- .../__init__.py | 0 .../default_consts.py | 59 +++++ .../manufacture.py | 237 ++++++++++++++++++ .../onie_install.sh | 94 +++++++ .../test_first_time_boot_password_change.py | 161 ++++++++++++ 5 files changed, 551 insertions(+) create mode 100644 tests/platform_tests/test_first_time_boot_password_change/__init__.py create mode 100644 tests/platform_tests/test_first_time_boot_password_change/default_consts.py create mode 100644 tests/platform_tests/test_first_time_boot_password_change/manufacture.py create mode 100644 tests/platform_tests/test_first_time_boot_password_change/onie_install.sh create mode 100644 tests/platform_tests/test_first_time_boot_password_change/test_first_time_boot_password_change.py diff --git a/tests/platform_tests/test_first_time_boot_password_change/__init__.py b/tests/platform_tests/test_first_time_boot_password_change/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/platform_tests/test_first_time_boot_password_change/default_consts.py b/tests/platform_tests/test_first_time_boot_password_change/default_consts.py new file mode 100644 index 0000000000..c89725021a --- /dev/null +++ b/tests/platform_tests/test_first_time_boot_password_change/default_consts.py @@ -0,0 +1,59 @@ +''' +This file contains the default consts used by the scripts on the same folder: +manufactue.py and test_first_time_boot_password_change.py +''' +class DefaultConsts: + ''' + @summary: a constants class used by the tests + ''' + DEFAULT_USER = 'admin' + DEFAULT_PASSWORD = 'YourPaSsWoRd' + DUMMY_PASSWORD = 'admin' + NEW_PASSWORD = 'Jg_GRK9BJB58s_5H' + ONIE_USER = 'root' + ONIE_PASSWORD = 'root' + + # connection command + SSH_COMMAND = 'ssh -tt -q -o ControlMaster=auto -o ControlPersist=60s -o ' \ + 'ControlPath=/tmp/ansible-ssh-%h-%p-%r -o StrictHostKeyChecking=no ' \ + '-o UserKnownHostsFile=/dev/null -o GSSAPIAuthentication=no ' \ + '-o PubkeyAuthentication=no -p 22 -l {} ' + + ANOTHER_SSH_COMMAND = 'ssh -l {} ' + + ORIGINAL = 'ssh -tt -q -o ControlMaster=auto -o ControlPersist=60s -o ' \ + 'ControlPath=/tmp/ansible-ssh-%h-%p-%r -o StrictHostKeyChecking=no ' \ + '-o UserKnownHostsFile=/dev/null -o GSSAPIAuthentication=no ' \ + '-o PubkeyAuthentication=no -p 22 -l {} ' + + SCP_COMMNAD = 'scp -o ControlMaster=auto ' \ + '-o ControlPersist=60s -o ControlPath=/tmp/ansible-ssh-%h-%p-%r' \ + ' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ' \ + 'GSSAPIAuthentication=no -o PubkeyAuthentication=no {} {}@{}:{}' + + ONIE_INSTALL_PATH = 'platform_tests/test_first_time_boot_password_change/onie_install.sh' + # expired password message regex + PASSWORD_REGEX = 'assword' + PERMISSION_DENID = '[Pp]ermission denied' + DEFAULT_PROMPT = ['$', '#'] + SONIC_PROMPT = '$' + LOGIN_INCORRECT = 'Login incorrect' + KEY_VERIFICATION_ERR = 'Host key verification failed ' + CONTINUE_CONNECTING = '(yes/no/[fingerprint])?' + LONG_PERIOD = 30 + APPLY_CONFIGURATIONS = 10 + SLEEP_AFTER_MANUFACTURE = 60 + NEW_PASSWORD_REGEX = 'New password' + RETYPE_PASSWORD_REGEX = 'Retype new password' + # expired password message regex + EXPIRED_PASSWORD_MSG = 'You are required to change your password immediately' + + # visual colors + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + + diff --git a/tests/platform_tests/test_first_time_boot_password_change/manufacture.py b/tests/platform_tests/test_first_time_boot_password_change/manufacture.py new file mode 100644 index 0000000000..77207cfc6d --- /dev/null +++ b/tests/platform_tests/test_first_time_boot_password_change/manufacture.py @@ -0,0 +1,237 @@ +''' +This is script will install a given image passed in the parameters +to the device using ONIE install mode. +Assumptions: + 1. The system is up + 2. Login username is 'admin' and default password: 'YourPaSsWoRd' + 3. ONIE system with either no password to enter ONIE cli or 'root' password + 4. Enough space to upload restore image to ONIE, otherwise it will fail + 5. "onie_insall.sh" script in the same folder as this script + 6. Existing image, will not check if the image path is existing, should be accessible without password! + +Detailed logic of manufacture script: + 1. Connect to dut + 2. upload the "onie_install.sh" file in the same folder to dut + 3. run the bash file, the script "onie_install.sh" is responsible for entering ONIE install mode + 4. upload image to ONIE + 5. install image using onie-nos-install +''' +import pexpect +import time +from ansible.module_utils.basic import * +import logging +from tests.platform_tests.test_first_time_boot_password_change.default_consts import DefaultConsts + + +logger = logging.getLogger(__name__) + + +def print_log(msg, color=''): + ''' + @summary: will print the msg with date format first + :param msg: msg to print + :param color: colot to print + :return: + ''' + logger.info(color + msg + DefaultConsts.ENDC) + + +def ping_till_alive(dut_ip, should_be_alive=True, timeout=300): + ''' + @summary: this function will ping system till alive, if specified otherwise + will ping till down + :param dut_ip: device under test ip address + :param should_be_alive: if True, will ping system till alive, if False will ping till down + :param timeout: fail if the desired state is not achieved + ''' + # create an engine + localhost_engine = pexpect.spawn('sudo su', env={'TERM': 'dumb'}) + localhost_engine.expect(['.*#', '.$']) + time_passed = 0 + result = 'Fail' + while time_passed <= timeout: + print_log("Pinging system {} till {}".format(dut_ip, 'alive' if should_be_alive else 'down')) + localhost_engine.sendline('ping -c 1 ' + dut_ip) + response = localhost_engine.expect(['1 packets received', '0 packets received']) + if response == 0: + if should_be_alive: + result = 'Success' + break + else: + if not should_be_alive: + result = 'Success' + break + + print_log("Sleeping 2 secs between pings") + time.sleep(2) + time_passed += 2 + + if result == 'Fail': + fail_msg = "Expected system to be {} after timeout of {} but the system was {}".format( + 'alive' if should_be_alive else 'down', + timeout, + 'down' if should_be_alive else 'alive' + ) + print_log(fail_msg) + localhost_engine.close() + + +def create_engine(dut_ip, username, password, timeout=30): + ''' + @summary: this command will create an ssh engine to run command + on the device under test + :param dut_ip: device under test ip address + :param username: user name to login + :param password: password for username + :param timeout: default timeout + ''' + print_log("Creating engine for {} with username: {} and password: {}".format(dut_ip, username, password)) + child = pexpect.spawn(DefaultConsts.SSH_COMMAND.format(username) + dut_ip, env={'TERM': 'dumb'}, timeout=timeout) + index = child.expect([DefaultConsts.PASSWORD_REGEX, + DefaultConsts.DEFAULT_PROMPT[0], + DefaultConsts.DEFAULT_PROMPT[1]]) + if index == 0: + child.sendline(password + '\r') + + print_log("Engine created successfully") + return child + + +def upload_file_to_dut(dut_ip, filename, destination, username, password, timeout=30): + ''' + @summary: this function will upload the given file to dut under destination folder + :param dut_ip: device under test + :param filename: path to filenmae + :param username: username of the device + :param password: password to username + :param timeout: timeout + ''' + print_log('Uploading file {} to dut {} under \'{}\' dir'.format(filename, dut_ip, destination)) + if timeout > DefaultConsts.LONG_PERIOD: + print_log('Please be patient this may take some time') + cmd = DefaultConsts.SCP_COMMNAD.format(filename, username, dut_ip, destination) + child = pexpect.spawn(cmd, timeout=timeout) + index = child.expect(["100%", + DefaultConsts.PASSWORD_REGEX]) + if index == 0: + print_log('Done Uploading file - 100%', DefaultConsts.OKGREEN) + return + + # enter password + child.sendline(password + '\r') + child.expect(['100%']) + print_log('Done Uploading file - 100%', DefaultConsts.OKGREEN) + + +def enter_onie_install_mode(dut_ip): + ''' + @summary: this function will upload the "onie_install.sh" bash script + found in the same folder of this script and run it from the dut, + the script "onie_install.sh" is used to load the the ONIE install mode after reboot, + for more info please read the documentation in the bash script. + :param dut_ip: device under test ip address + ''' + print_log("Entering ONIE install mode by running \"onie_install.sh\" bash script on DUT", + DefaultConsts.WARNING + DefaultConsts.BOLD) + upload_file_to_dut(dut_ip, DefaultConsts.ONIE_INSTALL_PATH, '/tmp', + DefaultConsts.DEFAULT_USER, + DefaultConsts.DEFAULT_PASSWORD) + # create ssh connection device + sonic_engine = create_engine(dut_ip, DefaultConsts.DEFAULT_USER, DefaultConsts.DEFAULT_PASSWORD) + sonic_engine.sendline('sudo su') + sonic_engine.expect('$') + sonic_engine.sendline('cd /tmp') + sonic_engine.expect('$') + print_log("Validating file \"{}\" existence".format(DefaultConsts.ONIE_INSTALL_PATH.split('/')[-1])) + # validate the file is there + sonic_engine.sendline('ls') + sonic_engine.expect('{}'.format(DefaultConsts.ONIE_INSTALL_PATH.split('/')[-1])) + # # change permissions + print_log("Executing the bash script uploaded") + sonic_engine.sendline('sudo chmod +777 onie_install.sh') + sonic_engine.expect('$') + sonic_engine.sendline('sudo ./onie_install.sh install') + sonic_engine.expect('Reboot will be done after 3 sec') + # # close session, the system will perform reboot + ping_till_alive(dut_ip, should_be_alive=False) + print_log("System is Down!", DefaultConsts.BOLD + DefaultConsts.OKGREEN) + sonic_engine.close() + + +def install_image_from_onie(dut_ip, restore_image_path): + ''' + @summary: this function will upload the restore image to ONIE and perform + install to the image using "onie-nos-install" + :param dut_ip: device under test ip address + :param restore_image_path: path to restore image should be in the format /../../../your_image_name.bin + ''' + ping_till_alive(dut_ip, should_be_alive=True) + print_log("System is UP!", DefaultConsts.BOLD + DefaultConsts.OKGREEN) + upload_file_to_dut(dut_ip, restore_image_path, '/', DefaultConsts.ONIE_USER, DefaultConsts.ONIE_PASSWORD, + timeout=420) + + restore_image_name = restore_image_path.split('/')[-1] + print_log('restore image name is {}'.format(restore_image_name)) + # SSH to ONIE + child = create_engine(dut_ip, DefaultConsts.ONIE_USER, DefaultConsts.ONIE_PASSWORD) + print_log("Install the image from ONIE") + child.sendline('cd /') + child.sendline('.*#') + child.sendline('onie-stop') + child.sendline('.*#') + child.sendline('onie-nos-install {}'.format(restore_image_name) +'\r') + print_log("Ping system till down") + ping_till_alive(dut_ip, should_be_alive=False) + print_log("Ping system till alive") + ping_till_alive(dut_ip, should_be_alive=True) + child.close() + + +def reboot_dut(dut_ip, username, password): + ''' + @summary: this function will reboot dut by executing "reboot" cmd from dut shell + and will wait till system is up again + :param dut_ip: device under test IP address + :param username: username of dut + :param password: passwod username + ''' + engine = pexpect.spawn(DefaultConsts.SSH_COMMAND.format(username) + dut_ip, timeout=60) + engine.expect(DefaultConsts.PASSWORD_REGEX) + engine.sendline(password) + engine.expect(DefaultConsts.DEFAULT_PROMPT) + print_log("Performing reboot to DUT {}".format(dut_ip)) + engine.sendline("sudo reboot") + ping_till_alive(dut_ip, should_be_alive=False) + ping_till_alive(dut_ip, should_be_alive=True) + + + +def manufacture(dut_ip, restore_image_path): + ''' + @summary: will remove the installed image and intsall the image given in the restore_image_path + Assumptions: + 1. The system is up + 2. Login username is 'admin' and default password: 'YourPaSsWoRd' + 3. ONIE system with either no password to enter ONIE cli or 'root' password + 4. Enough space to upload restore image to ONIE, otherwise it will fail, and will leave system in ONIE mode! + 5. "onie_insall.sh" script in the same folder as this script, + under "tests/platform_tests/test_first_time_boot_password_change" + 6. Existing image, will not check if the image path is existing, should be accessible without password! + Detailed logic of manufacture script: + 1. Connect to dut + 2. upload the "onie_install.sh" file in the same folder to dut + 3. run the bash file, the script "onie_install.sh" is responsible for entering ONIE install mode + 4. upload image to ONIE + 5. install image using onie-nos-install + :param dut_ip: device to manufacture + :param restore_image_path: path to the image + ''' + # create engine for the localhost running this script + print_log("Manufacture started", DefaultConsts.OKGREEN + DefaultConsts.BOLD) + # perform manufacture + enter_onie_install_mode( dut_ip) + install_image_from_onie(dut_ip, restore_image_path) + print_log("Sleeping for {} secs to stabilize system".format(DefaultConsts.SLEEP_AFTER_MANUFACTURE)) + time.sleep(DefaultConsts.SLEEP_AFTER_MANUFACTURE) + print_log("Manufacture is completed - SUCCESS", DefaultConsts.OKGREEN + DefaultConsts.BOLD) + diff --git a/tests/platform_tests/test_first_time_boot_password_change/onie_install.sh b/tests/platform_tests/test_first_time_boot_password_change/onie_install.sh new file mode 100644 index 0000000000..2c5b4012ab --- /dev/null +++ b/tests/platform_tests/test_first_time_boot_password_change/onie_install.sh @@ -0,0 +1,94 @@ +#!/bin/sh + +# By this script, SONiC switch moving to ONIE with specific boot_mode +# The examples of usage: +# onie_reboot.sh install +# onie_reboot.sh update + +onie_mount=/mnt/onie-boot +os_boot=/host +onie_partition= +onie_entry=0 +secure_boot_status= + +enable_onie_access() +{ + onie_partition=$(fdisk -l | grep "ONIE boot" | awk '{print $1}') + if [ ! -d $onie_mount ]; then + mkdir /mnt/onie-boot + fi + mount $onie_partition /mnt/onie-boot + if [ ! -e /lib/onie ]; then + ln -s /mnt/onie-boot/onie/tools/lib/onie /lib/onie + fi + PATH=/sbin:/usr/sbin:/bin:/usr/bin:$onie_mount/onie/tools/bin/ + export PATH +} + +clean_onie_access() +{ + rm -f /lib/onie + umount $onie_partition +} + +# ONIE entry must exist in grub config +find_onie_menuentry() +{ + onie_entry="$(cat $os_boot/grub/grub.cfg | grep -e 'menuentry' | cat -n | awk '$0~/ONIE/ {print $1-1}')" + entries_num="$(echo "$onie_entry" | grep -E '^[0-9]+$' | wc -l)" + if [ $entries_num -eq 1 ] && [ $onie_entry -ge 1 ]; then + return 0 + fi + return 1 +} + +change_grub_boot_order() +{ + find_onie_menuentry + rc=$? + if [ $rc -eq 0 ]; then + grub-reboot --boot-directory=$os_boot $onie_entry + else + echo "ERROR: ONIE entry wasn't found in grub config" + return 1 + fi + + echo "Set onie mode to $1" + grub-editenv $onie_mount/grub/grubenv set onie_mode=$1 + return 0 +} + +system_reboot() +{ + echo "Reboot will be done after 3 sec." + sleep 3 + /sbin/reboot +} + +check_secure_boot_enabled() +{ + secure_boot_status=$(bootctl | grep "Secure Boot" | awk '{print $3}') +} + + +check_secure_boot_enabled +rc=$? +if [ "$secure_boot_status" = "enabled" ]; then + onie_partition=$(fdisk -l | grep "EFI System" | awk '{print $1}') + if [ ! -d $onie_mount ]; then + mkdir /mnt/onie-boot + fi + mount $onie_partition /mnt/onie-boot + grub-editenv $onie_mount/EFI/debian/grubenv set next_entry=ONIE + umount $onie_partition +else + enable_onie_access + change_grub_boot_order $1 + clean_onie_access +fi + +if [ $rc -eq 0 ]; then + system_reboot +fi + +exit $rc diff --git a/tests/platform_tests/test_first_time_boot_password_change/test_first_time_boot_password_change.py b/tests/platform_tests/test_first_time_boot_password_change/test_first_time_boot_password_change.py new file mode 100644 index 0000000000..c04ff878db --- /dev/null +++ b/tests/platform_tests/test_first_time_boot_password_change/test_first_time_boot_password_change.py @@ -0,0 +1,161 @@ +''' +This test checks the default password change after initial reboot. +Due to new the california law, each user must change the default password. +Passwords such as: "admin", "12345", "root", etc. will no longer be accepted. + +Important Note: + Please run this test from sonic-mgmt/tests folder, otherwise it will fail! +''' +import pytest +import logging +import pexpect +import time +from tests.common.helpers.assertions import pytest_assert +from tests.platform_tests.test_first_time_boot_password_change.default_consts import DefaultConsts +from tests.platform_tests.test_first_time_boot_password_change.manufacture import manufacture, reboot_dut + +pytestmark = [ + pytest.mark.topology('any'), + pytest.mark.sanity_check(skip_sanity=True), + pytest.mark.disable_loganalyzer, + pytest.mark.skip_check_dut_health +] + + +class currentConfigurations: + ''' + @summary: this class will act database to save current configurations and changes, + it will help us understand the current state of the system + and we will be using it as a part of cleanup fixtures + ''' + def __init__(self): + self.currentPassword = DefaultConsts.DEFAULT_PASSWORD # initial password + + +logger = logging.getLogger(__name__) +currentConfigurations = currentConfigurations() + + +@pytest.fixture(scope='module', autouse=True) +def dut_hostname(request): + ''' + @summary: this function returns the hostname of the dut + ''' + hostname = request.config.getoption('--host-pattern') + logger.info("Hostname is {}".format(hostname)) + return hostname + + +@pytest.fixture(scope='module', autouse=True) +def prepare_system_for_first_boot(request): + ''' + @summary: will bring the system to first boot state + by installing the image given by the --restore_to_image + ''' + restore_image_path = request.config.getoption('restore_to_image') + hostname = request.config.getoption('--host-pattern') + pytest_assert(restore_image_path) + manufacture(hostname, restore_image_path) + + +def change_password(dut_hostname, username, current_password, new_password): + ''' + @summary: this function changes the password for the user logged to in the dut_shell on the dut + :param dut_ip: device under test + :param current_password: current password + :param new_password: new password + ''' + logger.info("Changing password for username:{} to password: {}".format(username, new_password)) + try: + # create a new ssh connection + engine = pexpect.spawn(DefaultConsts.SSH_COMMAND.format(username) + dut_hostname, timeout=15) + # because of race condition + engine.delaybeforesend = 0.2 + engine.delayafterclose = 0.5 + engine.expect(DefaultConsts.PASSWORD_REGEX) + engine.sendline(current_password + '\r') + engine.expect(DefaultConsts.SONIC_PROMPT) + engine.sendline('sudo touch azmy.txt') + engine.expect(DefaultConsts.SONIC_PROMPT) + engine.sendline('sudo usermod -p $(openssl passwd -1 {}) {}'.format(new_password, username) + '\r') + engine.expect(DefaultConsts.SONIC_PROMPT) + logger.info("Sleeping for {} secs to apply password change".format(DefaultConsts.APPLY_CONFIGURATIONS)) + time.sleep(DefaultConsts.APPLY_CONFIGURATIONS) + engine.sendline('exit') + engine.close() + except Exception as err: + logger.info('Got an exception while changing the password') + logger.info(str(err)) + + +@pytest.fixture(scope='function', autouse=True) +def restore_original_password(dut_hostname): + ''' + @summary: this function will restore the original password. + Edge cases need to consider with this function: + 1. If the test failed and the original password was not changed: don't do anything + 2. If the password was changed: need to revert it back to original + ''' + yield + logger.info("Restore original password") + change_password(dut_hostname, + DefaultConsts.DEFAULT_USER, + currentConfigurations.currentPassword, + DefaultConsts.DEFAULT_PASSWORD) + + +def test_default_password_change_after_first_boot(dut_hostname): + ''' + @summary: in this test case we want to validate the mandatory request of + password change after the first boot of the image. + According to a new law passed on the united states, default passwords + such as: "admin", "root", "12345", etc. are no longer accepted. + Test Flow: + 1.a message should after initial boot requesting password change for default user. + 2.a successful password change, will be tested by relogin + :param dut_hostname: name of device under test + ''' + logger.info("create ssh connection to device after inital boot") + engine = pexpect.spawn(DefaultConsts.SSH_COMMAND.format(DefaultConsts.DEFAULT_USER) + dut_hostname) + # to prevent race condition + engine.delaybeforesend = 0.2 + engine.delayafterclose = 0.5 + # it should require password so password will be sent + engine.expect(DefaultConsts.PASSWORD_REGEX) + engine.sendline(DefaultConsts.DEFAULT_PASSWORD) + # we should expect the expired password regex to appead + logger.info("Expecting expired message printed") + index = engine.expect([DefaultConsts.EXPIRED_PASSWORD_MSG, pexpect.TIMEOUT]) + if index != 0: + raise Exception("We did not catch the message of expired password after initial boot!\n" + "Consider this as a bug or a degradation") + logger.info('Entering current password after the expired message appeared') + engine.sendline(DefaultConsts.DEFAULT_PASSWORD + '\r') + # suggest new password + logger.info('Entering a new password, password used is {}'.format(DefaultConsts.NEW_PASSWORD)) + engine.expect(DefaultConsts.NEW_PASSWORD_REGEX) + engine.sendline(DefaultConsts.NEW_PASSWORD + '\r') + logger.info('Retyping the new password') + engine.expect(DefaultConsts.RETYPE_PASSWORD_REGEX) + engine.sendline(DefaultConsts.NEW_PASSWORD + '\r') + engine.expect(DefaultConsts.DEFAULT_PROMPT) + # will be used in cleanup later + currentConfigurations.currentPassword = DefaultConsts.NEW_PASSWORD + logger.info("Exit cli for the default user and re-eneter again and expect no password expire message") + # close the session + engine.close() + logger.info("Sleeping for {} secs to allow system update password".format(DefaultConsts.SLEEP_AFTER_MANUFACTURE)) + time.sleep(DefaultConsts.SLEEP_AFTER_MANUFACTURE) + logger.info("create a new ssh connection to device") + engine = pexpect.spawn(DefaultConsts.SSH_COMMAND + dut_hostname) + engine.delaybeforesend = 0.2 + engine.delayafterclose = 0.5 + # expect password + engine.expect(DefaultConsts.PASSWORD_REGEX) + # enter new password + engine.sendline(DefaultConsts.NEW_PASSWORD + '\r') + # we should not expect the expired password regex to appear again + index = engine.expect([DefaultConsts.EXPIRED_PASSWORD_MSG] + DefaultConsts.DEFAULT_PROMPT) + if index == 0: + raise Exception("We captured the expiring message again after updating a new password!\n") + engine.close() From 74a473b5a516072ebee400dbaf993994b443da1c Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Tue, 22 Nov 2022 08:48:54 +0200 Subject: [PATCH 27/28] Revert "Introducing new test case for default password change after initial" This reverts commit 5e9520d7020cd7a9ba534d5c1064b90bd761569f. --- .../__init__.py | 0 .../default_consts.py | 59 ----- .../manufacture.py | 237 ------------------ .../onie_install.sh | 94 ------- .../test_first_time_boot_password_change.py | 161 ------------ 5 files changed, 551 deletions(-) delete mode 100644 tests/platform_tests/test_first_time_boot_password_change/__init__.py delete mode 100644 tests/platform_tests/test_first_time_boot_password_change/default_consts.py delete mode 100644 tests/platform_tests/test_first_time_boot_password_change/manufacture.py delete mode 100644 tests/platform_tests/test_first_time_boot_password_change/onie_install.sh delete mode 100644 tests/platform_tests/test_first_time_boot_password_change/test_first_time_boot_password_change.py diff --git a/tests/platform_tests/test_first_time_boot_password_change/__init__.py b/tests/platform_tests/test_first_time_boot_password_change/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/platform_tests/test_first_time_boot_password_change/default_consts.py b/tests/platform_tests/test_first_time_boot_password_change/default_consts.py deleted file mode 100644 index c89725021a..0000000000 --- a/tests/platform_tests/test_first_time_boot_password_change/default_consts.py +++ /dev/null @@ -1,59 +0,0 @@ -''' -This file contains the default consts used by the scripts on the same folder: -manufactue.py and test_first_time_boot_password_change.py -''' -class DefaultConsts: - ''' - @summary: a constants class used by the tests - ''' - DEFAULT_USER = 'admin' - DEFAULT_PASSWORD = 'YourPaSsWoRd' - DUMMY_PASSWORD = 'admin' - NEW_PASSWORD = 'Jg_GRK9BJB58s_5H' - ONIE_USER = 'root' - ONIE_PASSWORD = 'root' - - # connection command - SSH_COMMAND = 'ssh -tt -q -o ControlMaster=auto -o ControlPersist=60s -o ' \ - 'ControlPath=/tmp/ansible-ssh-%h-%p-%r -o StrictHostKeyChecking=no ' \ - '-o UserKnownHostsFile=/dev/null -o GSSAPIAuthentication=no ' \ - '-o PubkeyAuthentication=no -p 22 -l {} ' - - ANOTHER_SSH_COMMAND = 'ssh -l {} ' - - ORIGINAL = 'ssh -tt -q -o ControlMaster=auto -o ControlPersist=60s -o ' \ - 'ControlPath=/tmp/ansible-ssh-%h-%p-%r -o StrictHostKeyChecking=no ' \ - '-o UserKnownHostsFile=/dev/null -o GSSAPIAuthentication=no ' \ - '-o PubkeyAuthentication=no -p 22 -l {} ' - - SCP_COMMNAD = 'scp -o ControlMaster=auto ' \ - '-o ControlPersist=60s -o ControlPath=/tmp/ansible-ssh-%h-%p-%r' \ - ' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ' \ - 'GSSAPIAuthentication=no -o PubkeyAuthentication=no {} {}@{}:{}' - - ONIE_INSTALL_PATH = 'platform_tests/test_first_time_boot_password_change/onie_install.sh' - # expired password message regex - PASSWORD_REGEX = 'assword' - PERMISSION_DENID = '[Pp]ermission denied' - DEFAULT_PROMPT = ['$', '#'] - SONIC_PROMPT = '$' - LOGIN_INCORRECT = 'Login incorrect' - KEY_VERIFICATION_ERR = 'Host key verification failed ' - CONTINUE_CONNECTING = '(yes/no/[fingerprint])?' - LONG_PERIOD = 30 - APPLY_CONFIGURATIONS = 10 - SLEEP_AFTER_MANUFACTURE = 60 - NEW_PASSWORD_REGEX = 'New password' - RETYPE_PASSWORD_REGEX = 'Retype new password' - # expired password message regex - EXPIRED_PASSWORD_MSG = 'You are required to change your password immediately' - - # visual colors - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - - diff --git a/tests/platform_tests/test_first_time_boot_password_change/manufacture.py b/tests/platform_tests/test_first_time_boot_password_change/manufacture.py deleted file mode 100644 index 77207cfc6d..0000000000 --- a/tests/platform_tests/test_first_time_boot_password_change/manufacture.py +++ /dev/null @@ -1,237 +0,0 @@ -''' -This is script will install a given image passed in the parameters -to the device using ONIE install mode. -Assumptions: - 1. The system is up - 2. Login username is 'admin' and default password: 'YourPaSsWoRd' - 3. ONIE system with either no password to enter ONIE cli or 'root' password - 4. Enough space to upload restore image to ONIE, otherwise it will fail - 5. "onie_insall.sh" script in the same folder as this script - 6. Existing image, will not check if the image path is existing, should be accessible without password! - -Detailed logic of manufacture script: - 1. Connect to dut - 2. upload the "onie_install.sh" file in the same folder to dut - 3. run the bash file, the script "onie_install.sh" is responsible for entering ONIE install mode - 4. upload image to ONIE - 5. install image using onie-nos-install -''' -import pexpect -import time -from ansible.module_utils.basic import * -import logging -from tests.platform_tests.test_first_time_boot_password_change.default_consts import DefaultConsts - - -logger = logging.getLogger(__name__) - - -def print_log(msg, color=''): - ''' - @summary: will print the msg with date format first - :param msg: msg to print - :param color: colot to print - :return: - ''' - logger.info(color + msg + DefaultConsts.ENDC) - - -def ping_till_alive(dut_ip, should_be_alive=True, timeout=300): - ''' - @summary: this function will ping system till alive, if specified otherwise - will ping till down - :param dut_ip: device under test ip address - :param should_be_alive: if True, will ping system till alive, if False will ping till down - :param timeout: fail if the desired state is not achieved - ''' - # create an engine - localhost_engine = pexpect.spawn('sudo su', env={'TERM': 'dumb'}) - localhost_engine.expect(['.*#', '.$']) - time_passed = 0 - result = 'Fail' - while time_passed <= timeout: - print_log("Pinging system {} till {}".format(dut_ip, 'alive' if should_be_alive else 'down')) - localhost_engine.sendline('ping -c 1 ' + dut_ip) - response = localhost_engine.expect(['1 packets received', '0 packets received']) - if response == 0: - if should_be_alive: - result = 'Success' - break - else: - if not should_be_alive: - result = 'Success' - break - - print_log("Sleeping 2 secs between pings") - time.sleep(2) - time_passed += 2 - - if result == 'Fail': - fail_msg = "Expected system to be {} after timeout of {} but the system was {}".format( - 'alive' if should_be_alive else 'down', - timeout, - 'down' if should_be_alive else 'alive' - ) - print_log(fail_msg) - localhost_engine.close() - - -def create_engine(dut_ip, username, password, timeout=30): - ''' - @summary: this command will create an ssh engine to run command - on the device under test - :param dut_ip: device under test ip address - :param username: user name to login - :param password: password for username - :param timeout: default timeout - ''' - print_log("Creating engine for {} with username: {} and password: {}".format(dut_ip, username, password)) - child = pexpect.spawn(DefaultConsts.SSH_COMMAND.format(username) + dut_ip, env={'TERM': 'dumb'}, timeout=timeout) - index = child.expect([DefaultConsts.PASSWORD_REGEX, - DefaultConsts.DEFAULT_PROMPT[0], - DefaultConsts.DEFAULT_PROMPT[1]]) - if index == 0: - child.sendline(password + '\r') - - print_log("Engine created successfully") - return child - - -def upload_file_to_dut(dut_ip, filename, destination, username, password, timeout=30): - ''' - @summary: this function will upload the given file to dut under destination folder - :param dut_ip: device under test - :param filename: path to filenmae - :param username: username of the device - :param password: password to username - :param timeout: timeout - ''' - print_log('Uploading file {} to dut {} under \'{}\' dir'.format(filename, dut_ip, destination)) - if timeout > DefaultConsts.LONG_PERIOD: - print_log('Please be patient this may take some time') - cmd = DefaultConsts.SCP_COMMNAD.format(filename, username, dut_ip, destination) - child = pexpect.spawn(cmd, timeout=timeout) - index = child.expect(["100%", - DefaultConsts.PASSWORD_REGEX]) - if index == 0: - print_log('Done Uploading file - 100%', DefaultConsts.OKGREEN) - return - - # enter password - child.sendline(password + '\r') - child.expect(['100%']) - print_log('Done Uploading file - 100%', DefaultConsts.OKGREEN) - - -def enter_onie_install_mode(dut_ip): - ''' - @summary: this function will upload the "onie_install.sh" bash script - found in the same folder of this script and run it from the dut, - the script "onie_install.sh" is used to load the the ONIE install mode after reboot, - for more info please read the documentation in the bash script. - :param dut_ip: device under test ip address - ''' - print_log("Entering ONIE install mode by running \"onie_install.sh\" bash script on DUT", - DefaultConsts.WARNING + DefaultConsts.BOLD) - upload_file_to_dut(dut_ip, DefaultConsts.ONIE_INSTALL_PATH, '/tmp', - DefaultConsts.DEFAULT_USER, - DefaultConsts.DEFAULT_PASSWORD) - # create ssh connection device - sonic_engine = create_engine(dut_ip, DefaultConsts.DEFAULT_USER, DefaultConsts.DEFAULT_PASSWORD) - sonic_engine.sendline('sudo su') - sonic_engine.expect('$') - sonic_engine.sendline('cd /tmp') - sonic_engine.expect('$') - print_log("Validating file \"{}\" existence".format(DefaultConsts.ONIE_INSTALL_PATH.split('/')[-1])) - # validate the file is there - sonic_engine.sendline('ls') - sonic_engine.expect('{}'.format(DefaultConsts.ONIE_INSTALL_PATH.split('/')[-1])) - # # change permissions - print_log("Executing the bash script uploaded") - sonic_engine.sendline('sudo chmod +777 onie_install.sh') - sonic_engine.expect('$') - sonic_engine.sendline('sudo ./onie_install.sh install') - sonic_engine.expect('Reboot will be done after 3 sec') - # # close session, the system will perform reboot - ping_till_alive(dut_ip, should_be_alive=False) - print_log("System is Down!", DefaultConsts.BOLD + DefaultConsts.OKGREEN) - sonic_engine.close() - - -def install_image_from_onie(dut_ip, restore_image_path): - ''' - @summary: this function will upload the restore image to ONIE and perform - install to the image using "onie-nos-install" - :param dut_ip: device under test ip address - :param restore_image_path: path to restore image should be in the format /../../../your_image_name.bin - ''' - ping_till_alive(dut_ip, should_be_alive=True) - print_log("System is UP!", DefaultConsts.BOLD + DefaultConsts.OKGREEN) - upload_file_to_dut(dut_ip, restore_image_path, '/', DefaultConsts.ONIE_USER, DefaultConsts.ONIE_PASSWORD, - timeout=420) - - restore_image_name = restore_image_path.split('/')[-1] - print_log('restore image name is {}'.format(restore_image_name)) - # SSH to ONIE - child = create_engine(dut_ip, DefaultConsts.ONIE_USER, DefaultConsts.ONIE_PASSWORD) - print_log("Install the image from ONIE") - child.sendline('cd /') - child.sendline('.*#') - child.sendline('onie-stop') - child.sendline('.*#') - child.sendline('onie-nos-install {}'.format(restore_image_name) +'\r') - print_log("Ping system till down") - ping_till_alive(dut_ip, should_be_alive=False) - print_log("Ping system till alive") - ping_till_alive(dut_ip, should_be_alive=True) - child.close() - - -def reboot_dut(dut_ip, username, password): - ''' - @summary: this function will reboot dut by executing "reboot" cmd from dut shell - and will wait till system is up again - :param dut_ip: device under test IP address - :param username: username of dut - :param password: passwod username - ''' - engine = pexpect.spawn(DefaultConsts.SSH_COMMAND.format(username) + dut_ip, timeout=60) - engine.expect(DefaultConsts.PASSWORD_REGEX) - engine.sendline(password) - engine.expect(DefaultConsts.DEFAULT_PROMPT) - print_log("Performing reboot to DUT {}".format(dut_ip)) - engine.sendline("sudo reboot") - ping_till_alive(dut_ip, should_be_alive=False) - ping_till_alive(dut_ip, should_be_alive=True) - - - -def manufacture(dut_ip, restore_image_path): - ''' - @summary: will remove the installed image and intsall the image given in the restore_image_path - Assumptions: - 1. The system is up - 2. Login username is 'admin' and default password: 'YourPaSsWoRd' - 3. ONIE system with either no password to enter ONIE cli or 'root' password - 4. Enough space to upload restore image to ONIE, otherwise it will fail, and will leave system in ONIE mode! - 5. "onie_insall.sh" script in the same folder as this script, - under "tests/platform_tests/test_first_time_boot_password_change" - 6. Existing image, will not check if the image path is existing, should be accessible without password! - Detailed logic of manufacture script: - 1. Connect to dut - 2. upload the "onie_install.sh" file in the same folder to dut - 3. run the bash file, the script "onie_install.sh" is responsible for entering ONIE install mode - 4. upload image to ONIE - 5. install image using onie-nos-install - :param dut_ip: device to manufacture - :param restore_image_path: path to the image - ''' - # create engine for the localhost running this script - print_log("Manufacture started", DefaultConsts.OKGREEN + DefaultConsts.BOLD) - # perform manufacture - enter_onie_install_mode( dut_ip) - install_image_from_onie(dut_ip, restore_image_path) - print_log("Sleeping for {} secs to stabilize system".format(DefaultConsts.SLEEP_AFTER_MANUFACTURE)) - time.sleep(DefaultConsts.SLEEP_AFTER_MANUFACTURE) - print_log("Manufacture is completed - SUCCESS", DefaultConsts.OKGREEN + DefaultConsts.BOLD) - diff --git a/tests/platform_tests/test_first_time_boot_password_change/onie_install.sh b/tests/platform_tests/test_first_time_boot_password_change/onie_install.sh deleted file mode 100644 index 2c5b4012ab..0000000000 --- a/tests/platform_tests/test_first_time_boot_password_change/onie_install.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/sh - -# By this script, SONiC switch moving to ONIE with specific boot_mode -# The examples of usage: -# onie_reboot.sh install -# onie_reboot.sh update - -onie_mount=/mnt/onie-boot -os_boot=/host -onie_partition= -onie_entry=0 -secure_boot_status= - -enable_onie_access() -{ - onie_partition=$(fdisk -l | grep "ONIE boot" | awk '{print $1}') - if [ ! -d $onie_mount ]; then - mkdir /mnt/onie-boot - fi - mount $onie_partition /mnt/onie-boot - if [ ! -e /lib/onie ]; then - ln -s /mnt/onie-boot/onie/tools/lib/onie /lib/onie - fi - PATH=/sbin:/usr/sbin:/bin:/usr/bin:$onie_mount/onie/tools/bin/ - export PATH -} - -clean_onie_access() -{ - rm -f /lib/onie - umount $onie_partition -} - -# ONIE entry must exist in grub config -find_onie_menuentry() -{ - onie_entry="$(cat $os_boot/grub/grub.cfg | grep -e 'menuentry' | cat -n | awk '$0~/ONIE/ {print $1-1}')" - entries_num="$(echo "$onie_entry" | grep -E '^[0-9]+$' | wc -l)" - if [ $entries_num -eq 1 ] && [ $onie_entry -ge 1 ]; then - return 0 - fi - return 1 -} - -change_grub_boot_order() -{ - find_onie_menuentry - rc=$? - if [ $rc -eq 0 ]; then - grub-reboot --boot-directory=$os_boot $onie_entry - else - echo "ERROR: ONIE entry wasn't found in grub config" - return 1 - fi - - echo "Set onie mode to $1" - grub-editenv $onie_mount/grub/grubenv set onie_mode=$1 - return 0 -} - -system_reboot() -{ - echo "Reboot will be done after 3 sec." - sleep 3 - /sbin/reboot -} - -check_secure_boot_enabled() -{ - secure_boot_status=$(bootctl | grep "Secure Boot" | awk '{print $3}') -} - - -check_secure_boot_enabled -rc=$? -if [ "$secure_boot_status" = "enabled" ]; then - onie_partition=$(fdisk -l | grep "EFI System" | awk '{print $1}') - if [ ! -d $onie_mount ]; then - mkdir /mnt/onie-boot - fi - mount $onie_partition /mnt/onie-boot - grub-editenv $onie_mount/EFI/debian/grubenv set next_entry=ONIE - umount $onie_partition -else - enable_onie_access - change_grub_boot_order $1 - clean_onie_access -fi - -if [ $rc -eq 0 ]; then - system_reboot -fi - -exit $rc diff --git a/tests/platform_tests/test_first_time_boot_password_change/test_first_time_boot_password_change.py b/tests/platform_tests/test_first_time_boot_password_change/test_first_time_boot_password_change.py deleted file mode 100644 index c04ff878db..0000000000 --- a/tests/platform_tests/test_first_time_boot_password_change/test_first_time_boot_password_change.py +++ /dev/null @@ -1,161 +0,0 @@ -''' -This test checks the default password change after initial reboot. -Due to new the california law, each user must change the default password. -Passwords such as: "admin", "12345", "root", etc. will no longer be accepted. - -Important Note: - Please run this test from sonic-mgmt/tests folder, otherwise it will fail! -''' -import pytest -import logging -import pexpect -import time -from tests.common.helpers.assertions import pytest_assert -from tests.platform_tests.test_first_time_boot_password_change.default_consts import DefaultConsts -from tests.platform_tests.test_first_time_boot_password_change.manufacture import manufacture, reboot_dut - -pytestmark = [ - pytest.mark.topology('any'), - pytest.mark.sanity_check(skip_sanity=True), - pytest.mark.disable_loganalyzer, - pytest.mark.skip_check_dut_health -] - - -class currentConfigurations: - ''' - @summary: this class will act database to save current configurations and changes, - it will help us understand the current state of the system - and we will be using it as a part of cleanup fixtures - ''' - def __init__(self): - self.currentPassword = DefaultConsts.DEFAULT_PASSWORD # initial password - - -logger = logging.getLogger(__name__) -currentConfigurations = currentConfigurations() - - -@pytest.fixture(scope='module', autouse=True) -def dut_hostname(request): - ''' - @summary: this function returns the hostname of the dut - ''' - hostname = request.config.getoption('--host-pattern') - logger.info("Hostname is {}".format(hostname)) - return hostname - - -@pytest.fixture(scope='module', autouse=True) -def prepare_system_for_first_boot(request): - ''' - @summary: will bring the system to first boot state - by installing the image given by the --restore_to_image - ''' - restore_image_path = request.config.getoption('restore_to_image') - hostname = request.config.getoption('--host-pattern') - pytest_assert(restore_image_path) - manufacture(hostname, restore_image_path) - - -def change_password(dut_hostname, username, current_password, new_password): - ''' - @summary: this function changes the password for the user logged to in the dut_shell on the dut - :param dut_ip: device under test - :param current_password: current password - :param new_password: new password - ''' - logger.info("Changing password for username:{} to password: {}".format(username, new_password)) - try: - # create a new ssh connection - engine = pexpect.spawn(DefaultConsts.SSH_COMMAND.format(username) + dut_hostname, timeout=15) - # because of race condition - engine.delaybeforesend = 0.2 - engine.delayafterclose = 0.5 - engine.expect(DefaultConsts.PASSWORD_REGEX) - engine.sendline(current_password + '\r') - engine.expect(DefaultConsts.SONIC_PROMPT) - engine.sendline('sudo touch azmy.txt') - engine.expect(DefaultConsts.SONIC_PROMPT) - engine.sendline('sudo usermod -p $(openssl passwd -1 {}) {}'.format(new_password, username) + '\r') - engine.expect(DefaultConsts.SONIC_PROMPT) - logger.info("Sleeping for {} secs to apply password change".format(DefaultConsts.APPLY_CONFIGURATIONS)) - time.sleep(DefaultConsts.APPLY_CONFIGURATIONS) - engine.sendline('exit') - engine.close() - except Exception as err: - logger.info('Got an exception while changing the password') - logger.info(str(err)) - - -@pytest.fixture(scope='function', autouse=True) -def restore_original_password(dut_hostname): - ''' - @summary: this function will restore the original password. - Edge cases need to consider with this function: - 1. If the test failed and the original password was not changed: don't do anything - 2. If the password was changed: need to revert it back to original - ''' - yield - logger.info("Restore original password") - change_password(dut_hostname, - DefaultConsts.DEFAULT_USER, - currentConfigurations.currentPassword, - DefaultConsts.DEFAULT_PASSWORD) - - -def test_default_password_change_after_first_boot(dut_hostname): - ''' - @summary: in this test case we want to validate the mandatory request of - password change after the first boot of the image. - According to a new law passed on the united states, default passwords - such as: "admin", "root", "12345", etc. are no longer accepted. - Test Flow: - 1.a message should after initial boot requesting password change for default user. - 2.a successful password change, will be tested by relogin - :param dut_hostname: name of device under test - ''' - logger.info("create ssh connection to device after inital boot") - engine = pexpect.spawn(DefaultConsts.SSH_COMMAND.format(DefaultConsts.DEFAULT_USER) + dut_hostname) - # to prevent race condition - engine.delaybeforesend = 0.2 - engine.delayafterclose = 0.5 - # it should require password so password will be sent - engine.expect(DefaultConsts.PASSWORD_REGEX) - engine.sendline(DefaultConsts.DEFAULT_PASSWORD) - # we should expect the expired password regex to appead - logger.info("Expecting expired message printed") - index = engine.expect([DefaultConsts.EXPIRED_PASSWORD_MSG, pexpect.TIMEOUT]) - if index != 0: - raise Exception("We did not catch the message of expired password after initial boot!\n" - "Consider this as a bug or a degradation") - logger.info('Entering current password after the expired message appeared') - engine.sendline(DefaultConsts.DEFAULT_PASSWORD + '\r') - # suggest new password - logger.info('Entering a new password, password used is {}'.format(DefaultConsts.NEW_PASSWORD)) - engine.expect(DefaultConsts.NEW_PASSWORD_REGEX) - engine.sendline(DefaultConsts.NEW_PASSWORD + '\r') - logger.info('Retyping the new password') - engine.expect(DefaultConsts.RETYPE_PASSWORD_REGEX) - engine.sendline(DefaultConsts.NEW_PASSWORD + '\r') - engine.expect(DefaultConsts.DEFAULT_PROMPT) - # will be used in cleanup later - currentConfigurations.currentPassword = DefaultConsts.NEW_PASSWORD - logger.info("Exit cli for the default user and re-eneter again and expect no password expire message") - # close the session - engine.close() - logger.info("Sleeping for {} secs to allow system update password".format(DefaultConsts.SLEEP_AFTER_MANUFACTURE)) - time.sleep(DefaultConsts.SLEEP_AFTER_MANUFACTURE) - logger.info("create a new ssh connection to device") - engine = pexpect.spawn(DefaultConsts.SSH_COMMAND + dut_hostname) - engine.delaybeforesend = 0.2 - engine.delayafterclose = 0.5 - # expect password - engine.expect(DefaultConsts.PASSWORD_REGEX) - # enter new password - engine.sendline(DefaultConsts.NEW_PASSWORD + '\r') - # we should not expect the expired password regex to appear again - index = engine.expect([DefaultConsts.EXPIRED_PASSWORD_MSG] + DefaultConsts.DEFAULT_PROMPT) - if index == 0: - raise Exception("We captured the expiring message again after updating a new password!\n") - engine.close() From 7bdf0f1b94d3966cec7259a67ef83c3a9e74a2d7 Mon Sep 17 00:00:00 2001 From: Azmy Ali Date: Tue, 22 Nov 2022 08:48:54 +0200 Subject: [PATCH 28/28] Revert "Introducing new test case for default password change after initial" This reverts commit 5e9520d7020cd7a9ba534d5c1064b90bd761569f. --- tests/platform_tests/test_secure_upgrade.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/platform_tests/test_secure_upgrade.py b/tests/platform_tests/test_secure_upgrade.py index 12493d9d52..7146de6c54 100644 --- a/tests/platform_tests/test_secure_upgrade.py +++ b/tests/platform_tests/test_secure_upgrade.py @@ -20,9 +20,7 @@ pytestmark = [ pytest.mark.topology('any'), - pytest.mark.sanity_check(skip_sanity=True), pytest.mark.disable_loganalyzer, - pytest.mark.skip_check_dut_health ] logger = logging.getLogger(__name__)