From 2583bce511ee8dd1690a59f558006f79941523c3 Mon Sep 17 00:00:00 2001 From: nnandigam Date: Mon, 11 Mar 2024 14:40:24 -0700 Subject: [PATCH 1/7] agent publish refactor --- .../orchestrator/lib/agent_test_suite.py | 22 ++++- tests_e2e/orchestrator/runbook.yml | 6 ++ tests_e2e/pipeline/pipeline.yml | 6 ++ tests_e2e/pipeline/scripts/execute_tests.sh | 4 + .../tests/agent_cgroups/agent_cgroups.py | 4 +- .../tests/agent_cgroups/agent_cpu_quota.py | 4 +- .../agent_ext_workflow/extension_workflow.py | 4 +- .../tests/agent_firewall/agent_firewall.py | 4 +- .../agent_persist_firewall.py | 4 +- .../tests/agent_publish/agent_publish.py | 73 +++++++++++----- tests_e2e/tests/agent_status/agent_status.py | 4 +- tests_e2e/tests/agent_update/rsm_update.py | 78 ++--------------- tests_e2e/tests/agent_update/self_update.py | 4 +- tests_e2e/tests/ext_cgroups/ext_cgroups.py | 4 +- .../tests/ext_sequencing/ext_sequencing.py | 4 +- tests_e2e/tests/lib/agent_test.py | 26 +++++- tests_e2e/tests/lib/agent_update_helpers.py | 87 +++++++++++++++++++ .../publish_hostname/publish_hostname.py | 4 +- .../recover_network_interface.py | 4 +- .../scripts/agent_publish-check_update.py | 31 ++++--- 20 files changed, 245 insertions(+), 132 deletions(-) create mode 100644 tests_e2e/tests/lib/agent_update_helpers.py diff --git a/tests_e2e/orchestrator/lib/agent_test_suite.py b/tests_e2e/orchestrator/lib/agent_test_suite.py index 022bfe830..dfa391cc0 100644 --- a/tests_e2e/orchestrator/lib/agent_test_suite.py +++ b/tests_e2e/orchestrator/lib/agent_test_suite.py @@ -149,6 +149,8 @@ def __init__(self, metadata: TestSuiteMetadata) -> None: self._test_suites: List[AgentTestSuite] # Test suites to execute in the environment + self._test_args: Dict[str, str] # Additional arguments pass to the test suite + self._cloud: str # Azure cloud where test VMs are located self._subscription_id: str # Azure subscription where test VMs are located self._location: str # Azure location (region) where test VMs are located @@ -209,6 +211,7 @@ def _initialize(self, environment: Environment, variables: Dict[str, Any], lisa_ self._environment_name = variables["c_env_name"] self._test_suites = variables["c_test_suites"] + self._test_args = variables["test_args"] self._cloud = variables["cloud"] self._subscription_id = variables["subscription_id"] @@ -565,6 +568,7 @@ def _execute(self) -> None: try: test_context = self._create_test_context() + test_args = self._get_test_args() if not self._skip_setup: try: @@ -578,7 +582,7 @@ def _execute(self) -> None: for suite in self._test_suites: log.info("Executing test suite %s", suite.name) self._lisa_log.info("Executing Test Suite %s", suite.name) - case_success, check_log_start_time = self._execute_test_suite(suite, test_context, check_log_start_time) + case_success, check_log_start_time = self._execute_test_suite(suite, test_context, test_args, check_log_start_time) test_suite_success = case_success and test_suite_success if not case_success: failed_cases.append(suite.name) @@ -613,7 +617,7 @@ def _execute(self) -> None: if not test_suite_success or unexpected_error: raise TestFailedException(self._environment_name, failed_cases) - def _execute_test_suite(self, suite: TestSuiteInfo, test_context: AgentTestContext, check_log_start_time: datetime.datetime) -> Tuple[bool, datetime.datetime]: + def _execute_test_suite(self, suite: TestSuiteInfo, test_context: AgentTestContext, test_args: Dict[str, str], check_log_start_time: datetime.datetime) -> Tuple[bool, datetime.datetime]: """ Executes the given test suite and returns a tuple of a bool indicating whether all the tests in the suite succeeded, and the timestamp that should be used for the next check of the agent log. @@ -645,7 +649,7 @@ def _execute_test_suite(self, suite: TestSuiteInfo, test_context: AgentTestConte test_success: bool = True - test_instance = test.test_class(test_context) + test_instance = test.test_class(test_context, test_args) try: test_instance.run() summary.append(f"[Passed] {test.name}") @@ -842,6 +846,18 @@ def _create_test_context(self,) -> AgentTestContext: username=self._user, identity_file=self._identity_file) + def _get_test_args(self) -> Dict[str, str]: + """ + Returns the arguments to be passed to the test classes + """ + test_args: Dict[str, str] = {} + if self._test_args == "": + return test_args + for arg in self._test_args.split(','): + key, value = arg.split('=') + test_args[key] = value + return test_args + @staticmethod def _mark_log_as_failed(): """ diff --git a/tests_e2e/orchestrator/runbook.yml b/tests_e2e/orchestrator/runbook.yml index 722ceba61..9365e4616 100644 --- a/tests_e2e/orchestrator/runbook.yml +++ b/tests_e2e/orchestrator/runbook.yml @@ -55,6 +55,12 @@ variable: - recover_network_interface # + # Additional arguments pass to the test suites + # + - name: test_args + value: "" + is_case_visible: true + # Parameters used to create test VMs # - name: subscription_id diff --git a/tests_e2e/pipeline/pipeline.yml b/tests_e2e/pipeline/pipeline.yml index 35d3fe4c1..8a7971456 100644 --- a/tests_e2e/pipeline/pipeline.yml +++ b/tests_e2e/pipeline/pipeline.yml @@ -19,6 +19,11 @@ parameters: type: string default: "-" + - name: test_args + displayName: Test Args (additional arguments pass to the test suites. Comma-separated list of key=value pairs) + type: string + default: "-" + - name: image displayName: Image (image/image set name, URN, or VHD) type: string @@ -121,6 +126,7 @@ jobs: KEEP_ENVIRONMENT: ${{ parameters.keep_environment }} LOCATION: ${{ parameters.location }} TEST_SUITES: ${{ parameters.test_suites }} + TEST_ARGS: ${{ parameters.test_args }} VM_SIZE: ${{ parameters.vm_size }} - bash: $(Build.SourcesDirectory)/tests_e2e/pipeline/scripts/collect_artifacts.sh diff --git a/tests_e2e/pipeline/scripts/execute_tests.sh b/tests_e2e/pipeline/scripts/execute_tests.sh index 9c185b333..d2d2f874c 100755 --- a/tests_e2e/pipeline/scripts/execute_tests.sh +++ b/tests_e2e/pipeline/scripts/execute_tests.sh @@ -54,6 +54,9 @@ if [[ $TEST_SUITES == "-" ]]; then else TEST_SUITES="-v test_suites:\"$TEST_SUITES\"" fi +if [[ $TEST_ARGS == "-" ]]; then + TEST_ARGS="" +fi if [[ $IMAGE == "-" ]]; then IMAGE="" fi @@ -92,4 +95,5 @@ docker run --rm \ -v location:\"$LOCATION\" \ -v vm_size:\"$VM_SIZE\" \ -v allow_ssh:\"$IP_ADDRESS\" \ + -v test_args:\"$TEST_ARGS\" \ $TEST_SUITES" diff --git a/tests_e2e/tests/agent_cgroups/agent_cgroups.py b/tests_e2e/tests/agent_cgroups/agent_cgroups.py index 449c5c362..20195a2a2 100644 --- a/tests_e2e/tests/agent_cgroups/agent_cgroups.py +++ b/tests_e2e/tests/agent_cgroups/agent_cgroups.py @@ -26,8 +26,8 @@ class AgentCgroups(AgentVmTest): This test verifies that the agent is running in the expected cgroups. """ - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._ssh_client = self._context.create_ssh_client() def run(self): diff --git a/tests_e2e/tests/agent_cgroups/agent_cpu_quota.py b/tests_e2e/tests/agent_cgroups/agent_cpu_quota.py index be66428b9..9124b58d8 100644 --- a/tests_e2e/tests/agent_cgroups/agent_cpu_quota.py +++ b/tests_e2e/tests/agent_cgroups/agent_cpu_quota.py @@ -9,8 +9,8 @@ class AgentCPUQuota(AgentVmTest): """ The test verify that the agent detects when it is throttled for using too much CPU, that it detects processes that do belong to the agent's cgroup, and that resource metrics are generated. """ - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._ssh_client = self._context.create_ssh_client() def run(self): diff --git a/tests_e2e/tests/agent_ext_workflow/extension_workflow.py b/tests_e2e/tests/agent_ext_workflow/extension_workflow.py index 3f25c6a6b..9bd27dae7 100644 --- a/tests_e2e/tests/agent_ext_workflow/extension_workflow.py +++ b/tests_e2e/tests/agent_ext_workflow/extension_workflow.py @@ -58,8 +58,8 @@ class ExtensionWorkflow(AgentVmTest): - Match the operation sequence as per the test and make sure they are in the correct chronological order - Restart the agent and verify if the correct operation sequence is followed """ - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._ssh_client = context.create_ssh_client() # This class represents the GuestAgentDcrTestExtension running on the test VM diff --git a/tests_e2e/tests/agent_firewall/agent_firewall.py b/tests_e2e/tests/agent_firewall/agent_firewall.py index c5b789dea..0801f8f4f 100644 --- a/tests_e2e/tests/agent_firewall/agent_firewall.py +++ b/tests_e2e/tests/agent_firewall/agent_firewall.py @@ -26,8 +26,8 @@ class AgentFirewall(AgentVmTest): This test verifies the agent firewall rules are added properly. It checks each firewall rule is present and working as expected. """ - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._ssh_client = self._context.create_ssh_client() def run(self): diff --git a/tests_e2e/tests/agent_persist_firewall/agent_persist_firewall.py b/tests_e2e/tests/agent_persist_firewall/agent_persist_firewall.py index 5bfeb403a..f1510cebb 100644 --- a/tests_e2e/tests/agent_persist_firewall/agent_persist_firewall.py +++ b/tests_e2e/tests/agent_persist_firewall/agent_persist_firewall.py @@ -27,8 +27,8 @@ class AgentPersistFirewallTest(AgentVmTest): This test verifies agent setup persist firewall rules using custom network setup service or firewalld service. Ensure those rules are added on boot and working as expected. """ - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._ssh_client: SshClient = self._context.create_ssh_client() def run(self): diff --git a/tests_e2e/tests/agent_publish/agent_publish.py b/tests_e2e/tests/agent_publish/agent_publish.py index 0cf51c331..2a090bf56 100644 --- a/tests_e2e/tests/agent_publish/agent_publish.py +++ b/tests_e2e/tests/agent_publish/agent_publish.py @@ -18,10 +18,10 @@ # import uuid from datetime import datetime -from typing import Any, Dict, List from tests_e2e.tests.lib.agent_test import AgentVmTest from tests_e2e.tests.lib.agent_test_context import AgentVmTestContext +from tests_e2e.tests.lib.agent_update_helpers import request_rsm_update from tests_e2e.tests.lib.vm_extension_identifier import VmExtensionIds, VmExtensionIdentifier from tests_e2e.tests.lib.logging import log from tests_e2e.tests.lib.ssh_client import SshClient @@ -33,9 +33,10 @@ class AgentPublishTest(AgentVmTest): This script verifies if the agent update performed in the vm. """ - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._ssh_client: SshClient = self._context.create_ssh_client() + self._published_version = self._test_args.get('publishedVersion', '9.9.9.9') def run(self): """ @@ -47,9 +48,17 @@ def run(self): 5. Ensure CSE is working """ self._get_agent_info() - self._prepare_agent() + + log.info("Testing rsm update flow....") + self._prepare_agent_for_rsm_update() + self._check_update() + self._get_agent_info() + + log.info("Testing self update flow....") + self._prepare_agent_for_self_update() self._check_update() self._get_agent_info() + self._check_cse() def get_ignore_errors_before_timestamp(self) -> datetime: @@ -60,14 +69,48 @@ def _get_agent_info(self) -> None: stdout: str = self._ssh_client.run_command("waagent-version", use_sudo=True) log.info('Agent info \n%s', stdout) - def _prepare_agent(self) -> None: + def _verify_agent_reported_supported_feature_flag(self): + """ + RSM update rely on supported feature flag that agent sends to CRP.So, checking if GA reports feature flag from reported status + """ + log.info( + "Executing verify_versioning_supported_feature.py remote script to verify agent reported supported feature flag, so that CRP can send RSM update request") + self._run_remote_test(self._ssh_client, "agent_update-verify_versioning_supported_feature.py", use_sudo=True) + log.info("Successfully verified that Agent reported VersioningGovernance supported feature flag") + + def _check_rsm_gs(self, requested_version: str) -> None: + # This checks if RSM GS available to the agent after we send the rsm update request + log.info( + 'Executing wait_for_rsm_gs.py remote script to verify latest GS contain requested version after rsm update requested') + self._run_remote_test(self._ssh_client, f"agent_update-wait_for_rsm_gs.py --version {requested_version}", + use_sudo=True) + log.info('Verified latest GS contain requested version after rsm update requested') + + def _prepare_agent_for_rsm_update(self) -> None: + """ + This method prepares the agent for the RSM update + """ + log.info( + 'Updating agent config flags to allow and download test versions') + output: str = self._ssh_client.run_command( + "update-waagent-conf AutoUpdate.UpdateToLatestVersion=y Debug.EnableGAVersioning=y AutoUpdate.GAFamily=Test", use_sudo=True) + log.info('Successfully updated agent update config \n %s', output) + + self._verify_agent_reported_supported_feature_flag() + request_rsm_update(self._published_version, self._context.vm) + self._check_rsm_gs(self._published_version) + + def _prepare_agent_for_self_update(self) -> None: + """ + This method prepares the agent for the self update + """ log.info("Modifying agent update related config flags and renaming the log file") - self._run_remote_test(self._ssh_client, "sh -c 'agent-service stop && mv /var/log/waagent.log /var/log/waagent.$(date --iso-8601=seconds).log && update-waagent-conf AutoUpdate.UpdateToLatestVersion=y AutoUpdate.GAFamily=Test AutoUpdate.Enabled=y Extensions.Enabled=y'", use_sudo=True) - log.info('Renamed log file and updated agent-update DownloadNewAgents GAFamily config flags') + self._run_remote_test(self._ssh_client, "sh -c 'agent-service stop && mv /var/log/waagent.log /var/log/waagent.$(date --iso-8601=seconds).log && rm -rf /var/lib/waagent/WALinuxAgent-* && update-waagent-conf AutoUpdate.UpdateToLatestVersion=y AutoUpdate.GAFamily=Test AutoUpdate.Enabled=y Extensions.Enabled=y Debug.EnableGAVersioning=n'", use_sudo=True) + log.info('Renamed log file and updated self-update config flags') def _check_update(self) -> None: log.info("Verifying for agent update status") - self._run_remote_test(self._ssh_client, "agent_publish-check_update.py") + self._run_remote_test(self._ssh_client, f"agent_publish-check_update.py --published-version {self._published_version}") log.info('Successfully checked the agent update') def _check_cse(self) -> None: @@ -86,20 +129,6 @@ def _check_cse(self) -> None: ) custom_script_2_1.assert_instance_view(expected_version="2.1", expected_message=message) - def get_ignore_error_rules(self) -> List[Dict[str, Any]]: - ignore_rules = [ - # - # This is expected as latest version can be the less than test version - # - # WARNING ExtHandler ExtHandler Agent WALinuxAgent-9.9.9.9 is permanently blacklisted - # - { - 'message': r"Agent WALinuxAgent-9.9.9.9 is permanently blacklisted" - } - - ] - return ignore_rules - if __name__ == "__main__": AgentPublishTest.run_from_command_line() diff --git a/tests_e2e/tests/agent_status/agent_status.py b/tests_e2e/tests/agent_status/agent_status.py index c02a3f4bf..63e140c81 100644 --- a/tests_e2e/tests/agent_status/agent_status.py +++ b/tests_e2e/tests/agent_status/agent_status.py @@ -38,8 +38,8 @@ class RetryableAgentStatusException(BaseException): class AgentStatus(AgentVmTest): - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._ssh_client = self._context.create_ssh_client() def validate_instance_view_vmagent_status(self, instance_view: VirtualMachineInstanceView): diff --git a/tests_e2e/tests/agent_update/rsm_update.py b/tests_e2e/tests/agent_update/rsm_update.py index 86ff7b5e9..3c5801902 100644 --- a/tests_e2e/tests/agent_update/rsm_update.py +++ b/tests_e2e/tests/agent_update/rsm_update.py @@ -23,28 +23,22 @@ # The test verifies agent update for rsm workflow. This test covers three scenarios downgrade, upgrade and no update. # For each scenario, we initiate the rsm request with target version and then verify agent updated to that target version. # -import json import re from typing import List, Dict, Any -import requests from assertpy import assert_that, fail -from azure.identity import DefaultAzureCredential -from azure.mgmt.compute.models import VirtualMachine -from msrestazure.azure_cloud import Cloud from tests_e2e.tests.lib.agent_test import AgentVmTest from tests_e2e.tests.lib.agent_test_context import AgentVmTestContext -from tests_e2e.tests.lib.azure_clouds import AZURE_CLOUDS +from tests_e2e.tests.lib.agent_update_helpers import request_rsm_update from tests_e2e.tests.lib.logging import log from tests_e2e.tests.lib.retry import retry_if_false -from tests_e2e.tests.lib.virtual_machine_client import VirtualMachineClient class RsmUpdateBvt(AgentVmTest): - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._ssh_client = self._context.create_ssh_client() self._installed_agent_version = "9.9.9.9" self._downgrade_version = "9.9.9.9" @@ -81,7 +75,7 @@ def run(self) -> None: log.info("Current agent version running on the vm before update is \n%s", stdout) self._downgrade_version: str = "2.3.15.0" log.info("Attempting downgrade version %s", self._downgrade_version) - self._request_rsm_update(self._downgrade_version) + request_rsm_update(self._downgrade_version, self._context.vm) self._check_rsm_gs(self._downgrade_version) self._prepare_agent() # Verify downgrade scenario @@ -94,7 +88,7 @@ def run(self) -> None: log.info("Current agent version running on the vm before update is \n%s", stdout) upgrade_version: str = "2.3.15.1" log.info("Attempting upgrade version %s", upgrade_version) - self._request_rsm_update(upgrade_version) + request_rsm_update(upgrade_version, self._context.vm) self._check_rsm_gs(upgrade_version) self._verify_guest_agent_update(upgrade_version) self._verify_agent_reported_update_status(upgrade_version) @@ -105,7 +99,7 @@ def run(self) -> None: log.info("Current agent version running on the vm before update is \n%s", stdout) current_version: str = "2.3.15.1" log.info("Attempting update version same as current version %s", current_version) - self._request_rsm_update(current_version) + request_rsm_update(current_version, self._context.vm) self._check_rsm_gs(current_version) self._verify_guest_agent_update(current_version) self._verify_agent_reported_update_status(current_version) @@ -117,7 +111,7 @@ def run(self) -> None: log.info("Current agent version running on the vm before update is \n%s", stdout) version: str = "1.5.0.0" log.info("Attempting requested version %s", version) - self._request_rsm_update(version) + request_rsm_update(version, self._context.vm) self._check_rsm_gs(version) self._verify_no_guest_agent_update(version) self._verify_agent_reported_update_status(version) @@ -146,64 +140,6 @@ def _prepare_agent(self) -> None: "update-waagent-conf AutoUpdate.UpdateToLatestVersion=y Debug.EnableGAVersioning=y AutoUpdate.GAFamily=Test", use_sudo=True) log.info('Successfully updated agent update config \n %s', output) - @staticmethod - def _verify_agent_update_flag_enabled(vm: VirtualMachineClient) -> bool: - result: VirtualMachine = vm.get_model() - flag: bool = result.os_profile.linux_configuration.enable_vm_agent_platform_updates - if flag is None: - return False - return flag - - def _enable_agent_update_flag(self, vm: VirtualMachineClient) -> None: - osprofile = { - "location": self._context.vm.location, # location is required field - "properties": { - "osProfile": { - "linuxConfiguration": { - "enableVMAgentPlatformUpdates": True - } - } - } - } - log.info("updating the vm with osProfile property:\n%s", osprofile) - vm.update(osprofile) - - def _request_rsm_update(self, requested_version: str) -> None: - """ - This method is to simulate the rsm request. - First we ensure the PlatformUpdates enabled in the vm and then make a request using rest api - """ - if not self._verify_agent_update_flag_enabled(self._context.vm): - # enable the flag - log.info("Attempting vm update to set the enableVMAgentPlatformUpdates flag") - self._enable_agent_update_flag(self._context.vm) - log.info("Updated the enableVMAgentPlatformUpdates flag to True") - else: - log.info("Already enableVMAgentPlatformUpdates flag set to True") - - cloud: Cloud = AZURE_CLOUDS[self._context.vm.cloud] - credential: DefaultAzureCredential = DefaultAzureCredential(authority=cloud.endpoints.active_directory) - token = credential.get_token(cloud.endpoints.resource_manager + "/.default") - headers = {'Authorization': 'Bearer ' + token.token, 'Content-Type': 'application/json'} - # Later this api call will be replaced by azure-python-sdk wrapper - base_url = cloud.endpoints.resource_manager - url = base_url + "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/virtualMachines/{2}/" \ - "UpgradeVMAgent?api-version=2022-08-01".format(self._context.vm.subscription, - self._context.vm.resource_group, - self._context.vm.name) - data = { - "target": "Microsoft.OSTCLinuxAgent.Test", - "targetVersion": requested_version - } - - log.info("Attempting rsm upgrade post request to endpoint: {0} with data: {1}".format(url, data)) - response = requests.post(url, data=json.dumps(data), headers=headers, timeout=300) - if response.status_code == 202: - log.info("RSM upgrade request accepted") - else: - raise Exception("Error occurred while making RSM upgrade request. Status code : {0} and msg: {1}".format( - response.status_code, response.content)) - def _verify_guest_agent_update(self, requested_version: str) -> None: """ Verify current agent version running on rsm requested version diff --git a/tests_e2e/tests/agent_update/self_update.py b/tests_e2e/tests/agent_update/self_update.py index 947c26ecc..2d522cc92 100644 --- a/tests_e2e/tests/agent_update/self_update.py +++ b/tests_e2e/tests/agent_update/self_update.py @@ -36,8 +36,8 @@ class SelfUpdateBvt(AgentVmTest): This test case is to verify that the agent can update itself to the latest version using self-update path when vm not enrolled to RSM updates """ - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._ssh_client = self._context.create_ssh_client() self._test_version = "2.8.9.9" self._test_pkg_name = f"WALinuxAgent-{self._test_version}.zip" diff --git a/tests_e2e/tests/ext_cgroups/ext_cgroups.py b/tests_e2e/tests/ext_cgroups/ext_cgroups.py index 94a0c9725..5a7c14dd0 100644 --- a/tests_e2e/tests/ext_cgroups/ext_cgroups.py +++ b/tests_e2e/tests/ext_cgroups/ext_cgroups.py @@ -27,8 +27,8 @@ class ExtCgroups(AgentVmTest): This test verifies the installed extensions assigned correctly in their cgroups. """ - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._ssh_client = self._context.create_ssh_client() def run(self): diff --git a/tests_e2e/tests/ext_sequencing/ext_sequencing.py b/tests_e2e/tests/ext_sequencing/ext_sequencing.py index b2b3b9a70..8d53cd17f 100644 --- a/tests_e2e/tests/ext_sequencing/ext_sequencing.py +++ b/tests_e2e/tests/ext_sequencing/ext_sequencing.py @@ -45,8 +45,8 @@ class ExtSequencing(AgentVmssTest): - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._scenario_start = datetime.min # Cases to test different dependency scenarios diff --git a/tests_e2e/tests/lib/agent_test.py b/tests_e2e/tests/lib/agent_test.py index 0021a8d74..10159463f 100644 --- a/tests_e2e/tests/lib/agent_test.py +++ b/tests_e2e/tests/lib/agent_test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 - +import argparse # Microsoft Azure Linux Agent # # Copyright 2018 Microsoft Corporation @@ -49,8 +49,9 @@ class AgentTest(ABC): """ Abstract base class for Agent tests """ - def __init__(self, context: AgentTestContext): + def __init__(self, context: AgentTestContext, test_args: Dict[str, str]): self._context: AgentTestContext = context + self._test_args: Dict[str, str] = test_args @abstractmethod def run(self): @@ -76,9 +77,9 @@ def run_from_command_line(cls): """ try: if issubclass(cls, AgentVmTest): - cls(AgentVmTestContext.from_args()).run() + cls(AgentVmTestContext.from_args(), cls._cmd_line_test_args()).run() elif issubclass(cls, AgentVmssTest): - cls(AgentVmssTestContext.from_args()).run() + cls(AgentVmssTestContext.from_args(), cls._cmd_line_test_args()).run() else: raise Exception(f"Class {cls.__name__} is not a valid test class") except SystemExit: # Bad arguments @@ -92,6 +93,7 @@ def run_from_command_line(cls): sys.exit(0) + def _run_remote_test(self, ssh_client: SshClient, command: str, use_sudo: bool = False, attempts: int = ATTEMPTS, attempt_delay: int = ATTEMPT_DELAY) -> None: """ Derived classes can use this method to execute a remote test (a test that runs over SSH). @@ -108,6 +110,22 @@ def _run_remote_test(self, ssh_client: SshClient, command: str, use_sudo: bool = def _indent(text: str, indent: str = " " * 8): return "\n".join(f"{indent}{line}" for line in text.splitlines()) + @staticmethod + def _cmd_line_test_args() -> Dict[str, str]: + """ + A method to read the test case arguments from command-line. + """ + parser = argparse.ArgumentParser() + parser.add_argument('-test-args', '--test-args', required=False, help="Test case arguments", default="") + args = parser.parse_args() + test_args: Dict[str, str] = {} + if args.test_args == "": + return test_args + for arg in args.test_args.split(","): + key, value = arg.split("=") + test_args[key] = value + return test_args + class AgentVmTest(AgentTest): """ diff --git a/tests_e2e/tests/lib/agent_update_helpers.py b/tests_e2e/tests/lib/agent_update_helpers.py new file mode 100644 index 000000000..3cdd118da --- /dev/null +++ b/tests_e2e/tests/lib/agent_update_helpers.py @@ -0,0 +1,87 @@ +# +# Copyright 2018 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import json + +import requests +from azure.identity import DefaultAzureCredential +from msrestazure.azure_cloud import Cloud +from azure.mgmt.compute.models import VirtualMachine + +from tests_e2e.tests.lib.azure_clouds import AZURE_CLOUDS +from tests_e2e.tests.lib.logging import log +from tests_e2e.tests.lib.virtual_machine_client import VirtualMachineClient + +# Helper methods for agent update/publish tests + + +def verify_agent_update_flag_enabled(vm: VirtualMachineClient) -> bool: + result: VirtualMachine = vm.get_model() + flag: bool = result.os_profile.linux_configuration.enable_vm_agent_platform_updates + if flag is None: + return False + return flag + + +def enable_agent_update_flag(vm: VirtualMachineClient) -> None: + osprofile = { + "location": vm.location, # location is required field + "properties": { + "osProfile": { + "linuxConfiguration": { + "enableVMAgentPlatformUpdates": True + } + } + } + } + log.info("updating the vm with osProfile property:\n%s", osprofile) + vm.update(osprofile) + + +def request_rsm_update(requested_version: str, vm: VirtualMachineClient) -> None: + """ + This method is to simulate the rsm request. + First we ensure the PlatformUpdates enabled in the vm and then make a request using rest api + """ + if not verify_agent_update_flag_enabled(vm): + # enable the flag + log.info("Attempting vm update to set the enableVMAgentPlatformUpdates flag") + enable_agent_update_flag(vm) + log.info("Updated the enableVMAgentPlatformUpdates flag to True") + else: + log.info("Already enableVMAgentPlatformUpdates flag set to True") + + cloud: Cloud = AZURE_CLOUDS[vm.cloud] + credential: DefaultAzureCredential = DefaultAzureCredential(authority=cloud.endpoints.active_directory) + token = credential.get_token(cloud.endpoints.resource_manager + "/.default") + headers = {'Authorization': 'Bearer ' + token.token, 'Content-Type': 'application/json'} + # Later this api call will be replaced by azure-python-sdk wrapper + base_url = cloud.endpoints.resource_manager + url = base_url + "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/virtualMachines/{2}/" \ + "UpgradeVMAgent?api-version=2022-08-01".format(vm.subscription, + vm.resource_group, + vm.name) + data = { + "target": "Microsoft.OSTCLinuxAgent.Test", + "targetVersion": requested_version + } + + log.info("Attempting rsm upgrade post request to endpoint: {0} with data: {1}".format(url, data)) + response = requests.post(url, data=json.dumps(data), headers=headers, timeout=300) + if response.status_code == 202: + log.info("RSM upgrade request accepted") + else: + raise Exception("Error occurred while making RSM upgrade request. Status code : {0} and msg: {1}".format( + response.status_code, response.content)) \ No newline at end of file diff --git a/tests_e2e/tests/publish_hostname/publish_hostname.py b/tests_e2e/tests/publish_hostname/publish_hostname.py index 19f7b10b4..c7ebad451 100644 --- a/tests_e2e/tests/publish_hostname/publish_hostname.py +++ b/tests_e2e/tests/publish_hostname/publish_hostname.py @@ -38,8 +38,8 @@ class PublishHostname(AgentVmTest): - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._context = context self._ssh_client = context.create_ssh_client() self._private_ip = context.vm.get_private_ip_address() diff --git a/tests_e2e/tests/recover_network_interface/recover_network_interface.py b/tests_e2e/tests/recover_network_interface/recover_network_interface.py index 39799d375..0d51ff559 100644 --- a/tests_e2e/tests/recover_network_interface/recover_network_interface.py +++ b/tests_e2e/tests/recover_network_interface/recover_network_interface.py @@ -37,8 +37,8 @@ class RecoverNetworkInterface(AgentVmTest): - def __init__(self, context: AgentVmTestContext): - super().__init__(context) + def __init__(self, context: AgentVmTestContext, test_args: dict): + super().__init__(context, test_args) self._context = context self._ssh_client = context.create_ssh_client() self._private_ip = context.vm.get_private_ip_address() diff --git a/tests_e2e/tests/scripts/agent_publish-check_update.py b/tests_e2e/tests/scripts/agent_publish-check_update.py index 38ae00a90..ab5eb7356 100755 --- a/tests_e2e/tests/scripts/agent_publish-check_update.py +++ b/tests_e2e/tests/scripts/agent_publish-check_update.py @@ -1,5 +1,5 @@ #!/usr/bin/env pypy3 - +import argparse # Microsoft Azure Linux Agent # # Copyright 2018 Microsoft Corporation @@ -53,6 +53,13 @@ """ _UPDATE_PATTERN_03 = re.compile(r'(.*Agent) update found, exiting current process to (\S*) to the new Agent version (\S*)') +""" +Current Agent 2.8.9.9 completed all update checks, exiting current process to upgrade to the new Agent version 2.10.0.7 +('2.8.9.9', 'upgrade', '2.10.0.7') +""" +_UPDATE_PATTERN_04 = re.compile(r'Current Agent (\S*) completed all update checks, exiting current process to (\S*) to the new Agent version (\S*)') + + """ > Agent WALinuxAgent-2.2.47 is running as the goal state agent ('2.2.47',) @@ -60,7 +67,7 @@ _RUNNING_PATTERN_00 = re.compile(r'.*Agent\sWALinuxAgent-(\S*)\sis running as the goal state agent') -def verify_agent_update_from_log(): +def verify_agent_update_from_log(published_version: str) -> bool: exit_code = 0 detected_update = False @@ -73,16 +80,17 @@ def verify_agent_update_from_log(): if 'TelemetryData' in record.text: continue - for p in [_UPDATE_PATTERN_00, _UPDATE_PATTERN_01, _UPDATE_PATTERN_02, _UPDATE_PATTERN_03]: - update_match = re.match(p, record.text) + for p in [_UPDATE_PATTERN_00, _UPDATE_PATTERN_01, _UPDATE_PATTERN_02, _UPDATE_PATTERN_03, _UPDATE_PATTERN_04]: + update_match = re.match(p, record.message) if update_match: - detected_update = True update_version = update_match.groups()[2] - log.info('found the agent update log: %s', record.text) - break + if update_version == published_version: + detected_update = True + log.info('found the agent update log: %s', record.text) + break if detected_update: - running_match = re.match(_RUNNING_PATTERN_00, record.text) + running_match = re.match(_RUNNING_PATTERN_00, record.message) if running_match and update_version == running_match.groups()[0]: update_successful = True log.info('found the agent started new version log: %s', record.text) @@ -95,7 +103,7 @@ def verify_agent_update_from_log(): log.warning('update was not successful') exit_code = 1 else: - log.warning('update was not detected') + log.warning('update was not detected for version: %s', published_version) exit_code = 1 return exit_code == 0 @@ -103,7 +111,10 @@ def verify_agent_update_from_log(): # This method will trace agent update messages in the agent log and determine if the update was successful or not. def main(): - found: bool = retry_if_false(verify_agent_update_from_log) + parser = argparse.ArgumentParser() + parser.add_argument('-p', '--published-version', required=True) + args = parser.parse_args() + found: bool = retry_if_false(lambda: verify_agent_update_from_log(args.published_version)) if not found: fail('update was not found in the logs') From b9acb77a6c438462e07e886125633ecf51087648 Mon Sep 17 00:00:00 2001 From: nnandigam Date: Wed, 13 Mar 2024 15:25:27 -0700 Subject: [PATCH 2/7] support arm 64vm --- tests_e2e/tests/agent_publish/agent_publish.py | 11 +++++++++-- tests_e2e/tests/agent_update/rsm_update.py | 9 +++++---- tests_e2e/tests/lib/agent_update_helpers.py | 16 +++++++++++----- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/tests_e2e/tests/agent_publish/agent_publish.py b/tests_e2e/tests/agent_publish/agent_publish.py index 2a090bf56..e755c0c56 100644 --- a/tests_e2e/tests/agent_publish/agent_publish.py +++ b/tests_e2e/tests/agent_publish/agent_publish.py @@ -90,16 +90,23 @@ def _prepare_agent_for_rsm_update(self) -> None: """ This method prepares the agent for the RSM update """ + # First we update the agent to latest version like prod + # Next send RSM update request for new published test version log.info( 'Updating agent config flags to allow and download test versions') output: str = self._ssh_client.run_command( - "update-waagent-conf AutoUpdate.UpdateToLatestVersion=y Debug.EnableGAVersioning=y AutoUpdate.GAFamily=Test", use_sudo=True) + "update-waagent-conf AutoUpdate.Enabled=y AutoUpdate.UpdateToLatestVersion=y", use_sudo=True) log.info('Successfully updated agent update config \n %s', output) self._verify_agent_reported_supported_feature_flag() - request_rsm_update(self._published_version, self._context.vm) + arch_type = self._ssh_client.get_architecture() + request_rsm_update(self._published_version, self._context.vm, arch_type) self._check_rsm_gs(self._published_version) + output: str = self._ssh_client.run_command( + "update-waagent-conf Debug.EnableGAVersioning=y AutoUpdate.GAFamily=Test", use_sudo=True) + log.info('Successfully enabled rsm updates \n %s', output) + def _prepare_agent_for_self_update(self) -> None: """ This method prepares the agent for the self update diff --git a/tests_e2e/tests/agent_update/rsm_update.py b/tests_e2e/tests/agent_update/rsm_update.py index 3c5801902..0c69c1df3 100644 --- a/tests_e2e/tests/agent_update/rsm_update.py +++ b/tests_e2e/tests/agent_update/rsm_update.py @@ -65,6 +65,7 @@ def get_ignore_error_rules(self) -> List[Dict[str, Any]]: return ignore_rules def run(self) -> None: + arch_type = self._ssh_client.get_architecture() # retrieve the installed agent version in the vm before run the scenario self._retrieve_installed_agent_version() # Allow agent to send supported feature flag @@ -75,7 +76,7 @@ def run(self) -> None: log.info("Current agent version running on the vm before update is \n%s", stdout) self._downgrade_version: str = "2.3.15.0" log.info("Attempting downgrade version %s", self._downgrade_version) - request_rsm_update(self._downgrade_version, self._context.vm) + request_rsm_update(self._downgrade_version, self._context.vm, arch_type) self._check_rsm_gs(self._downgrade_version) self._prepare_agent() # Verify downgrade scenario @@ -88,7 +89,7 @@ def run(self) -> None: log.info("Current agent version running on the vm before update is \n%s", stdout) upgrade_version: str = "2.3.15.1" log.info("Attempting upgrade version %s", upgrade_version) - request_rsm_update(upgrade_version, self._context.vm) + request_rsm_update(upgrade_version, self._context.vm, arch_type) self._check_rsm_gs(upgrade_version) self._verify_guest_agent_update(upgrade_version) self._verify_agent_reported_update_status(upgrade_version) @@ -99,7 +100,7 @@ def run(self) -> None: log.info("Current agent version running on the vm before update is \n%s", stdout) current_version: str = "2.3.15.1" log.info("Attempting update version same as current version %s", current_version) - request_rsm_update(current_version, self._context.vm) + request_rsm_update(current_version, self._context.vm, arch_type) self._check_rsm_gs(current_version) self._verify_guest_agent_update(current_version) self._verify_agent_reported_update_status(current_version) @@ -111,7 +112,7 @@ def run(self) -> None: log.info("Current agent version running on the vm before update is \n%s", stdout) version: str = "1.5.0.0" log.info("Attempting requested version %s", version) - request_rsm_update(version, self._context.vm) + request_rsm_update(version, self._context.vm, arch_type) self._check_rsm_gs(version) self._verify_no_guest_agent_update(version) self._verify_agent_reported_update_status(version) diff --git a/tests_e2e/tests/lib/agent_update_helpers.py b/tests_e2e/tests/lib/agent_update_helpers.py index 3cdd118da..d48d47bf4 100644 --- a/tests_e2e/tests/lib/agent_update_helpers.py +++ b/tests_e2e/tests/lib/agent_update_helpers.py @@ -50,7 +50,7 @@ def enable_agent_update_flag(vm: VirtualMachineClient) -> None: vm.update(osprofile) -def request_rsm_update(requested_version: str, vm: VirtualMachineClient) -> None: +def request_rsm_update(requested_version: str, vm: VirtualMachineClient, arch_type) -> None: """ This method is to simulate the rsm request. First we ensure the PlatformUpdates enabled in the vm and then make a request using rest api @@ -73,10 +73,16 @@ def request_rsm_update(requested_version: str, vm: VirtualMachineClient) -> None "UpgradeVMAgent?api-version=2022-08-01".format(vm.subscription, vm.resource_group, vm.name) - data = { - "target": "Microsoft.OSTCLinuxAgent.Test", - "targetVersion": requested_version - } + if arch_type == "aarch64": + data = { + "target": "Microsoft.OSTCLinuxAgent.ARM64Test", + "targetVersion": requested_version + } + else: + data = { + "target": "Microsoft.OSTCLinuxAgent.Test", + "targetVersion": requested_version, + } log.info("Attempting rsm upgrade post request to endpoint: {0} with data: {1}".format(url, data)) response = requests.post(url, data=json.dumps(data), headers=headers, timeout=300) From fa61288cac3283174f7d2e0051d8e39bc92b3b50 Mon Sep 17 00:00:00 2001 From: Nageswara Nandigam Date: Wed, 13 Mar 2024 18:31:21 -0700 Subject: [PATCH 3/7] convert dict to str --- tests_e2e/orchestrator/lib/agent_test_suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests_e2e/orchestrator/lib/agent_test_suite.py b/tests_e2e/orchestrator/lib/agent_test_suite.py index dfa391cc0..b62ec1b13 100644 --- a/tests_e2e/orchestrator/lib/agent_test_suite.py +++ b/tests_e2e/orchestrator/lib/agent_test_suite.py @@ -149,7 +149,7 @@ def __init__(self, metadata: TestSuiteMetadata) -> None: self._test_suites: List[AgentTestSuite] # Test suites to execute in the environment - self._test_args: Dict[str, str] # Additional arguments pass to the test suite + self._test_args: str # Additional arguments pass to the test suite self._cloud: str # Azure cloud where test VMs are located self._subscription_id: str # Azure subscription where test VMs are located From 4aa33e556f8d61f57313f6078de5004676d35a31 Mon Sep 17 00:00:00 2001 From: nnandigam Date: Sat, 23 Mar 2024 22:23:54 -0700 Subject: [PATCH 4/7] address comments --- .../orchestrator/lib/agent_test_suite.py | 30 +++++---- .../tests/agent_cgroups/agent_cgroups.py | 4 +- .../tests/agent_cgroups/agent_cpu_quota.py | 4 +- .../agent_ext_workflow/extension_workflow.py | 4 +- .../tests/agent_firewall/agent_firewall.py | 4 +- .../agent_persist_firewall.py | 4 +- .../tests/agent_publish/agent_publish.py | 63 +++++++++++++++---- tests_e2e/tests/agent_status/agent_status.py | 4 +- tests_e2e/tests/agent_update/rsm_update.py | 4 +- tests_e2e/tests/agent_update/self_update.py | 4 +- tests_e2e/tests/ext_cgroups/ext_cgroups.py | 4 +- .../tests/ext_sequencing/ext_sequencing.py | 4 +- tests_e2e/tests/lib/agent_test.py | 23 +------ .../publish_hostname/publish_hostname.py | 4 +- .../recover_network_interface.py | 4 +- 15 files changed, 96 insertions(+), 68 deletions(-) diff --git a/tests_e2e/orchestrator/lib/agent_test_suite.py b/tests_e2e/orchestrator/lib/agent_test_suite.py index b62ec1b13..799ea441b 100644 --- a/tests_e2e/orchestrator/lib/agent_test_suite.py +++ b/tests_e2e/orchestrator/lib/agent_test_suite.py @@ -149,7 +149,7 @@ def __init__(self, metadata: TestSuiteMetadata) -> None: self._test_suites: List[AgentTestSuite] # Test suites to execute in the environment - self._test_args: str # Additional arguments pass to the test suite + self._test_args: Dict[str, str] # Additional arguments pass to the test suite self._cloud: str # Azure cloud where test VMs are located self._subscription_id: str # Azure subscription where test VMs are located @@ -211,7 +211,7 @@ def _initialize(self, environment: Environment, variables: Dict[str, Any], lisa_ self._environment_name = variables["c_env_name"] self._test_suites = variables["c_test_suites"] - self._test_args = variables["test_args"] + self._test_args = self._get_test_args(variables["test_args"]) self._cloud = variables["cloud"] self._subscription_id = variables["subscription_id"] @@ -568,7 +568,6 @@ def _execute(self) -> None: try: test_context = self._create_test_context() - test_args = self._get_test_args() if not self._skip_setup: try: @@ -582,7 +581,7 @@ def _execute(self) -> None: for suite in self._test_suites: log.info("Executing test suite %s", suite.name) self._lisa_log.info("Executing Test Suite %s", suite.name) - case_success, check_log_start_time = self._execute_test_suite(suite, test_context, test_args, check_log_start_time) + case_success, check_log_start_time = self._execute_test_suite(suite, test_context, check_log_start_time) test_suite_success = case_success and test_suite_success if not case_success: failed_cases.append(suite.name) @@ -617,7 +616,7 @@ def _execute(self) -> None: if not test_suite_success or unexpected_error: raise TestFailedException(self._environment_name, failed_cases) - def _execute_test_suite(self, suite: TestSuiteInfo, test_context: AgentTestContext, test_args: Dict[str, str], check_log_start_time: datetime.datetime) -> Tuple[bool, datetime.datetime]: + def _execute_test_suite(self, suite: TestSuiteInfo, test_context: AgentTestContext, check_log_start_time: datetime.datetime) -> Tuple[bool, datetime.datetime]: """ Executes the given test suite and returns a tuple of a bool indicating whether all the tests in the suite succeeded, and the timestamp that should be used for the next check of the agent log. @@ -649,7 +648,7 @@ def _execute_test_suite(self, suite: TestSuiteInfo, test_context: AgentTestConte test_success: bool = True - test_instance = test.test_class(test_context, test_args) + test_instance = test.test_class(test_context) try: test_instance.run() summary.append(f"[Passed] {test.name}") @@ -816,12 +815,15 @@ def _create_test_context(self,) -> AgentTestContext: subscription=self._subscription_id, resource_group=self._resource_group_name, name=self._vm_name) - return AgentVmTestContext( + vm_test_context = AgentVmTestContext( working_directory=self._working_directory, vm=vm, ip_address=self._vm_ip_address, username=self._user, identity_file=self._identity_file) + for key in self._test_args: + setattr(vm_test_context, key, self._test_args[key]) + return vm_test_context else: log.info("Creating test context for scale set") if self._create_scale_set: @@ -840,21 +842,25 @@ def _create_test_context(self,) -> AgentTestContext: if self._create_scale_set: self._test_nodes = [_TestNode(name=i.instance_name, ip_address=i.ip_address) for i in scale_set.get_instances_ip_address()] - return AgentVmssTestContext( + vmss_test_context = AgentVmssTestContext( working_directory=self._working_directory, vmss=scale_set, username=self._user, identity_file=self._identity_file) + for key in self._test_args: + setattr(vmss_test_context, key, self._test_args[key]) + return vmss_test_context - def _get_test_args(self) -> Dict[str, str]: + @staticmethod + def _get_test_args(arg_str) -> Dict[str, str]: """ Returns the arguments to be passed to the test classes """ test_args: Dict[str, str] = {} - if self._test_args == "": + if arg_str == "": return test_args - for arg in self._test_args.split(','): - key, value = arg.split('=') + for arg in arg_str.split(','): + key, value = map(str.strip, arg.split('=')) test_args[key] = value return test_args diff --git a/tests_e2e/tests/agent_cgroups/agent_cgroups.py b/tests_e2e/tests/agent_cgroups/agent_cgroups.py index 20195a2a2..449c5c362 100644 --- a/tests_e2e/tests/agent_cgroups/agent_cgroups.py +++ b/tests_e2e/tests/agent_cgroups/agent_cgroups.py @@ -26,8 +26,8 @@ class AgentCgroups(AgentVmTest): This test verifies that the agent is running in the expected cgroups. """ - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._ssh_client = self._context.create_ssh_client() def run(self): diff --git a/tests_e2e/tests/agent_cgroups/agent_cpu_quota.py b/tests_e2e/tests/agent_cgroups/agent_cpu_quota.py index 9124b58d8..be66428b9 100644 --- a/tests_e2e/tests/agent_cgroups/agent_cpu_quota.py +++ b/tests_e2e/tests/agent_cgroups/agent_cpu_quota.py @@ -9,8 +9,8 @@ class AgentCPUQuota(AgentVmTest): """ The test verify that the agent detects when it is throttled for using too much CPU, that it detects processes that do belong to the agent's cgroup, and that resource metrics are generated. """ - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._ssh_client = self._context.create_ssh_client() def run(self): diff --git a/tests_e2e/tests/agent_ext_workflow/extension_workflow.py b/tests_e2e/tests/agent_ext_workflow/extension_workflow.py index 9bd27dae7..3f25c6a6b 100644 --- a/tests_e2e/tests/agent_ext_workflow/extension_workflow.py +++ b/tests_e2e/tests/agent_ext_workflow/extension_workflow.py @@ -58,8 +58,8 @@ class ExtensionWorkflow(AgentVmTest): - Match the operation sequence as per the test and make sure they are in the correct chronological order - Restart the agent and verify if the correct operation sequence is followed """ - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._ssh_client = context.create_ssh_client() # This class represents the GuestAgentDcrTestExtension running on the test VM diff --git a/tests_e2e/tests/agent_firewall/agent_firewall.py b/tests_e2e/tests/agent_firewall/agent_firewall.py index 0801f8f4f..c5b789dea 100644 --- a/tests_e2e/tests/agent_firewall/agent_firewall.py +++ b/tests_e2e/tests/agent_firewall/agent_firewall.py @@ -26,8 +26,8 @@ class AgentFirewall(AgentVmTest): This test verifies the agent firewall rules are added properly. It checks each firewall rule is present and working as expected. """ - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._ssh_client = self._context.create_ssh_client() def run(self): diff --git a/tests_e2e/tests/agent_persist_firewall/agent_persist_firewall.py b/tests_e2e/tests/agent_persist_firewall/agent_persist_firewall.py index f1510cebb..5bfeb403a 100644 --- a/tests_e2e/tests/agent_persist_firewall/agent_persist_firewall.py +++ b/tests_e2e/tests/agent_persist_firewall/agent_persist_firewall.py @@ -27,8 +27,8 @@ class AgentPersistFirewallTest(AgentVmTest): This test verifies agent setup persist firewall rules using custom network setup service or firewalld service. Ensure those rules are added on boot and working as expected. """ - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._ssh_client: SshClient = self._context.create_ssh_client() def run(self): diff --git a/tests_e2e/tests/agent_publish/agent_publish.py b/tests_e2e/tests/agent_publish/agent_publish.py index e755c0c56..8c76790bf 100644 --- a/tests_e2e/tests/agent_publish/agent_publish.py +++ b/tests_e2e/tests/agent_publish/agent_publish.py @@ -19,9 +19,13 @@ import uuid from datetime import datetime +from assertpy import fail + +from azurelinuxagent.common.version import AGENT_VERSION from tests_e2e.tests.lib.agent_test import AgentVmTest from tests_e2e.tests.lib.agent_test_context import AgentVmTestContext from tests_e2e.tests.lib.agent_update_helpers import request_rsm_update +from tests_e2e.tests.lib.retry import retry_if_false from tests_e2e.tests.lib.vm_extension_identifier import VmExtensionIds, VmExtensionIdentifier from tests_e2e.tests.lib.logging import log from tests_e2e.tests.lib.ssh_client import SshClient @@ -33,31 +37,31 @@ class AgentPublishTest(AgentVmTest): This script verifies if the agent update performed in the vm. """ - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._ssh_client: SshClient = self._context.create_ssh_client() - self._published_version = self._test_args.get('publishedVersion', '9.9.9.9') + self._published_version = self._get_published_version() def run(self): """ we run the scenario in the following steps: 1. Print the current agent version before the update 2. Prepare the agent for the update - 3. Check for agent update from the log - 4. Print the agent version after the update - 5. Ensure CSE is working + 3. Check for agent update from the log and waagent version + 4. Ensure CSE is working """ self._get_agent_info() log.info("Testing rsm update flow....") self._prepare_agent_for_rsm_update() - self._check_update() - self._get_agent_info() + self._check_update_from_log() + self._verify_current_agent_version() + self._check_cse() log.info("Testing self update flow....") self._prepare_agent_for_self_update() - self._check_update() - self._get_agent_info() + self._check_update_from_log() + self._verify_current_agent_version() self._check_cse() @@ -65,6 +69,15 @@ def get_ignore_errors_before_timestamp(self) -> datetime: timestamp = self._ssh_client.run_command("agent_publish-get_agent_log_record_timestamp.py") return datetime.strptime(timestamp.strip(), u'%Y-%m-%d %H:%M:%S.%f') + def _get_published_version(self): + """ + Get the published version that needs to be validated + Read from test_args if provided, else use the release version from version.py + """ + if hasattr(self._context, "published_version"): + return self._context.published_version + return AGENT_VERSION + def _get_agent_info(self) -> None: stdout: str = self._ssh_client.run_command("waagent-version", use_sudo=True) log.info('Agent info \n%s', stdout) @@ -112,14 +125,40 @@ def _prepare_agent_for_self_update(self) -> None: This method prepares the agent for the self update """ log.info("Modifying agent update related config flags and renaming the log file") - self._run_remote_test(self._ssh_client, "sh -c 'agent-service stop && mv /var/log/waagent.log /var/log/waagent.$(date --iso-8601=seconds).log && rm -rf /var/lib/waagent/WALinuxAgent-* && update-waagent-conf AutoUpdate.UpdateToLatestVersion=y AutoUpdate.GAFamily=Test AutoUpdate.Enabled=y Extensions.Enabled=y Debug.EnableGAVersioning=n'", use_sudo=True) + setup_script = ("agent-service stop && mv /var/log/waagent.log /var/log/waagent.$(date --iso-8601=seconds).log && " + "rm -rf /var/lib/waagent/WALinuxAgent-* && " + "update-waagent-conf AutoUpdate.UpdateToLatestVersion=y AutoUpdate.GAFamily=Test AutoUpdate.Enabled=y Extensions.Enabled=y Debug.EnableGAVersioning=n") + self._run_remote_test(self._ssh_client, f"sh -c '{setup_script}'", use_sudo=True) log.info('Renamed log file and updated self-update config flags') - def _check_update(self) -> None: + def _check_update_from_log(self) -> None: log.info("Verifying for agent update status") self._run_remote_test(self._ssh_client, f"agent_publish-check_update.py --published-version {self._published_version}") log.info('Successfully checked the agent update') + def _verify_current_agent_version(self) -> None: + """ + Verify current agent version running on published version + """ + + def _check_agent_version(version: str) -> bool: + waagent_version: str = self._ssh_client.run_command("waagent-version", use_sudo=True) + expected_version = f"Goal state agent: {version}" + if expected_version in waagent_version: + return True + else: + return False + + waagent_version: str = "" + log.info("Verifying agent updated to published version: {0}".format(self._published_version)) + success: bool = retry_if_false(lambda: _check_agent_version(self._published_version)) + if not success: + fail("Guest agent didn't update to published version {0} but found \n {1}. \n ".format( + self._published_version, waagent_version)) + waagent_version: str = self._ssh_client.run_command("waagent-version", use_sudo=True) + log.info( + f"Successfully verified agent updated to published version. Current agent version running:\n {waagent_version}") + def _check_cse(self) -> None: custom_script_2_1 = VirtualMachineExtensionClient( self._context.vm, diff --git a/tests_e2e/tests/agent_status/agent_status.py b/tests_e2e/tests/agent_status/agent_status.py index 63e140c81..c02a3f4bf 100644 --- a/tests_e2e/tests/agent_status/agent_status.py +++ b/tests_e2e/tests/agent_status/agent_status.py @@ -38,8 +38,8 @@ class RetryableAgentStatusException(BaseException): class AgentStatus(AgentVmTest): - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._ssh_client = self._context.create_ssh_client() def validate_instance_view_vmagent_status(self, instance_view: VirtualMachineInstanceView): diff --git a/tests_e2e/tests/agent_update/rsm_update.py b/tests_e2e/tests/agent_update/rsm_update.py index 0c69c1df3..ad2222d11 100644 --- a/tests_e2e/tests/agent_update/rsm_update.py +++ b/tests_e2e/tests/agent_update/rsm_update.py @@ -37,8 +37,8 @@ class RsmUpdateBvt(AgentVmTest): - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._ssh_client = self._context.create_ssh_client() self._installed_agent_version = "9.9.9.9" self._downgrade_version = "9.9.9.9" diff --git a/tests_e2e/tests/agent_update/self_update.py b/tests_e2e/tests/agent_update/self_update.py index 2d522cc92..947c26ecc 100644 --- a/tests_e2e/tests/agent_update/self_update.py +++ b/tests_e2e/tests/agent_update/self_update.py @@ -36,8 +36,8 @@ class SelfUpdateBvt(AgentVmTest): This test case is to verify that the agent can update itself to the latest version using self-update path when vm not enrolled to RSM updates """ - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._ssh_client = self._context.create_ssh_client() self._test_version = "2.8.9.9" self._test_pkg_name = f"WALinuxAgent-{self._test_version}.zip" diff --git a/tests_e2e/tests/ext_cgroups/ext_cgroups.py b/tests_e2e/tests/ext_cgroups/ext_cgroups.py index 5a7c14dd0..94a0c9725 100644 --- a/tests_e2e/tests/ext_cgroups/ext_cgroups.py +++ b/tests_e2e/tests/ext_cgroups/ext_cgroups.py @@ -27,8 +27,8 @@ class ExtCgroups(AgentVmTest): This test verifies the installed extensions assigned correctly in their cgroups. """ - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._ssh_client = self._context.create_ssh_client() def run(self): diff --git a/tests_e2e/tests/ext_sequencing/ext_sequencing.py b/tests_e2e/tests/ext_sequencing/ext_sequencing.py index 8d53cd17f..b2b3b9a70 100644 --- a/tests_e2e/tests/ext_sequencing/ext_sequencing.py +++ b/tests_e2e/tests/ext_sequencing/ext_sequencing.py @@ -45,8 +45,8 @@ class ExtSequencing(AgentVmssTest): - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._scenario_start = datetime.min # Cases to test different dependency scenarios diff --git a/tests_e2e/tests/lib/agent_test.py b/tests_e2e/tests/lib/agent_test.py index 10159463f..d85460610 100644 --- a/tests_e2e/tests/lib/agent_test.py +++ b/tests_e2e/tests/lib/agent_test.py @@ -49,9 +49,8 @@ class AgentTest(ABC): """ Abstract base class for Agent tests """ - def __init__(self, context: AgentTestContext, test_args: Dict[str, str]): + def __init__(self, context: AgentTestContext): self._context: AgentTestContext = context - self._test_args: Dict[str, str] = test_args @abstractmethod def run(self): @@ -77,9 +76,9 @@ def run_from_command_line(cls): """ try: if issubclass(cls, AgentVmTest): - cls(AgentVmTestContext.from_args(), cls._cmd_line_test_args()).run() + cls(AgentVmTestContext.from_args()).run() elif issubclass(cls, AgentVmssTest): - cls(AgentVmssTestContext.from_args(), cls._cmd_line_test_args()).run() + cls(AgentVmssTestContext.from_args()).run() else: raise Exception(f"Class {cls.__name__} is not a valid test class") except SystemExit: # Bad arguments @@ -110,22 +109,6 @@ def _run_remote_test(self, ssh_client: SshClient, command: str, use_sudo: bool = def _indent(text: str, indent: str = " " * 8): return "\n".join(f"{indent}{line}" for line in text.splitlines()) - @staticmethod - def _cmd_line_test_args() -> Dict[str, str]: - """ - A method to read the test case arguments from command-line. - """ - parser = argparse.ArgumentParser() - parser.add_argument('-test-args', '--test-args', required=False, help="Test case arguments", default="") - args = parser.parse_args() - test_args: Dict[str, str] = {} - if args.test_args == "": - return test_args - for arg in args.test_args.split(","): - key, value = arg.split("=") - test_args[key] = value - return test_args - class AgentVmTest(AgentTest): """ diff --git a/tests_e2e/tests/publish_hostname/publish_hostname.py b/tests_e2e/tests/publish_hostname/publish_hostname.py index c7ebad451..19f7b10b4 100644 --- a/tests_e2e/tests/publish_hostname/publish_hostname.py +++ b/tests_e2e/tests/publish_hostname/publish_hostname.py @@ -38,8 +38,8 @@ class PublishHostname(AgentVmTest): - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._context = context self._ssh_client = context.create_ssh_client() self._private_ip = context.vm.get_private_ip_address() diff --git a/tests_e2e/tests/recover_network_interface/recover_network_interface.py b/tests_e2e/tests/recover_network_interface/recover_network_interface.py index 0d51ff559..39799d375 100644 --- a/tests_e2e/tests/recover_network_interface/recover_network_interface.py +++ b/tests_e2e/tests/recover_network_interface/recover_network_interface.py @@ -37,8 +37,8 @@ class RecoverNetworkInterface(AgentVmTest): - def __init__(self, context: AgentVmTestContext, test_args: dict): - super().__init__(context, test_args) + def __init__(self, context: AgentVmTestContext): + super().__init__(context) self._context = context self._ssh_client = context.create_ssh_client() self._private_ip = context.vm.get_private_ip_address() From f8490b65095b08a6ca44abe8846ddb695393a196 Mon Sep 17 00:00:00 2001 From: nnandigam Date: Sun, 24 Mar 2024 18:47:36 -0700 Subject: [PATCH 5/7] pylint --- tests_e2e/tests/agent_publish/agent_publish.py | 3 +-- tests_e2e/tests/lib/agent_test.py | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests_e2e/tests/agent_publish/agent_publish.py b/tests_e2e/tests/agent_publish/agent_publish.py index 8c76790bf..b05d717a5 100644 --- a/tests_e2e/tests/agent_publish/agent_publish.py +++ b/tests_e2e/tests/agent_publish/agent_publish.py @@ -71,8 +71,7 @@ def get_ignore_errors_before_timestamp(self) -> datetime: def _get_published_version(self): """ - Get the published version that needs to be validated - Read from test_args if provided, else use the release version from version.py + Returns version from test_args if provided, else use the release version from version.py """ if hasattr(self._context, "published_version"): return self._context.published_version diff --git a/tests_e2e/tests/lib/agent_test.py b/tests_e2e/tests/lib/agent_test.py index d85460610..4f41103d3 100644 --- a/tests_e2e/tests/lib/agent_test.py +++ b/tests_e2e/tests/lib/agent_test.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -import argparse + # Microsoft Azure Linux Agent # # Copyright 2018 Microsoft Corporation @@ -73,6 +73,8 @@ def run_from_command_line(cls): """ Convenience method to execute the test when it is being invoked directly from the command line (as opposed as being invoked from a test framework or library.) + + Todo: Need to implement for reading test specific arguments from command line """ try: if issubclass(cls, AgentVmTest): @@ -92,7 +94,6 @@ def run_from_command_line(cls): sys.exit(0) - def _run_remote_test(self, ssh_client: SshClient, command: str, use_sudo: bool = False, attempts: int = ATTEMPTS, attempt_delay: int = ATTEMPT_DELAY) -> None: """ Derived classes can use this method to execute a remote test (a test that runs over SSH). From 528e6e3e7df00bcfae33b6279ff977c58ba1b41d Mon Sep 17 00:00:00 2001 From: nnandigam Date: Wed, 27 Mar 2024 10:00:53 -0700 Subject: [PATCH 6/7] new comments --- tests_e2e/tests/agent_publish/agent_publish.py | 7 ++++--- tests_e2e/tests/lib/agent_test.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tests_e2e/tests/agent_publish/agent_publish.py b/tests_e2e/tests/agent_publish/agent_publish.py index b05d717a5..f964557dc 100644 --- a/tests_e2e/tests/agent_publish/agent_publish.py +++ b/tests_e2e/tests/agent_publish/agent_publish.py @@ -21,7 +21,6 @@ from assertpy import fail -from azurelinuxagent.common.version import AGENT_VERSION from tests_e2e.tests.lib.agent_test import AgentVmTest from tests_e2e.tests.lib.agent_test_context import AgentVmTestContext from tests_e2e.tests.lib.agent_update_helpers import request_rsm_update @@ -71,11 +70,13 @@ def get_ignore_errors_before_timestamp(self) -> datetime: def _get_published_version(self): """ - Returns version from test_args if provided, else use the release version from version.py + Returns version from test_args if provided, else use the release version from source code version.py """ if hasattr(self._context, "published_version"): return self._context.published_version - return AGENT_VERSION + + version = self._ssh_client.run_command("pypy3 -c 'from azurelinuxagent.common.version import AGENT_VERSION; print(AGENT_VERSION)'").rstrip() + return version def _get_agent_info(self) -> None: stdout: str = self._ssh_client.run_command("waagent-version", use_sudo=True) diff --git a/tests_e2e/tests/lib/agent_test.py b/tests_e2e/tests/lib/agent_test.py index 4f41103d3..e4f73d725 100644 --- a/tests_e2e/tests/lib/agent_test.py +++ b/tests_e2e/tests/lib/agent_test.py @@ -74,7 +74,7 @@ def run_from_command_line(cls): Convenience method to execute the test when it is being invoked directly from the command line (as opposed as being invoked from a test framework or library.) - Todo: Need to implement for reading test specific arguments from command line + TODO: Need to implement for reading test specific arguments from command line """ try: if issubclass(cls, AgentVmTest): From bad260ce4b95f50b7b62acf3dc75b2d629a48303 Mon Sep 17 00:00:00 2001 From: nnandigam Date: Wed, 27 Mar 2024 10:17:12 -0700 Subject: [PATCH 7/7] updated comment --- tests_e2e/tests/agent_publish/agent_publish.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests_e2e/tests/agent_publish/agent_publish.py b/tests_e2e/tests/agent_publish/agent_publish.py index f964557dc..83c3f7160 100644 --- a/tests_e2e/tests/agent_publish/agent_publish.py +++ b/tests_e2e/tests/agent_publish/agent_publish.py @@ -70,7 +70,7 @@ def get_ignore_errors_before_timestamp(self) -> datetime: def _get_published_version(self): """ - Returns version from test_args if provided, else use the release version from source code version.py + Gets version from test_args if provided, else use the release version from source code version.py """ if hasattr(self._context, "published_version"): return self._context.published_version