diff --git a/azurelinuxagent/common/event.py b/azurelinuxagent/common/event.py index b11b468e53..01970fb209 100644 --- a/azurelinuxagent/common/event.py +++ b/azurelinuxagent/common/event.py @@ -28,6 +28,7 @@ import azurelinuxagent.common.logger as logger from azurelinuxagent.common.exception import EventError from azurelinuxagent.common.future import ustr +from azurelinuxagent.common.protocol.wire import CONTAINER_ID_ENV_VARIABLE from azurelinuxagent.common.protocol.restapi import TelemetryEventParam, \ TelemetryEvent, \ get_properties @@ -94,6 +95,7 @@ class WALAEventOperation: WALAEventOperation.UnInstall, ] + class EventStatus(object): EVENT_STATUS_FILE = "event_status.json" @@ -277,6 +279,8 @@ def _add_event(self, duration, evt_type, is_internal, is_success, message, name, event.parameters.append(TelemetryEventParam('Message', message)) event.parameters.append(TelemetryEventParam('Duration', duration)) event.parameters.append(TelemetryEventParam('ExtensionType', evt_type)) + event.parameters.append(TelemetryEventParam('ContainerId', + os.environ.get(CONTAINER_ID_ENV_VARIABLE, "UNINITIALIZED"))) data = get_properties(event) try: diff --git a/azurelinuxagent/common/protocol/wire.py b/azurelinuxagent/common/protocol/wire.py index d7d1f2fc1d..56e5e8c1e8 100644 --- a/azurelinuxagent/common/protocol/wire.py +++ b/azurelinuxagent/common/protocol/wire.py @@ -56,6 +56,8 @@ AGENTS_MANIFEST_FILE_NAME = "{0}.{1}.agentsManifest" TRANSPORT_CERT_FILE_NAME = "TransportCert.pem" TRANSPORT_PRV_FILE_NAME = "TransportPrivate.pem" +# Store the last retrieved container id as an environment variable to be shared between threads for telemetry purposes +CONTAINER_ID_ENV_VARIABLE = "AZURE_GUEST_AGENT_CONTAINER_ID" PROTOCOL_VERSION = "2012-11-30" ENDPOINT_FINE_NAME = "WireServer" @@ -112,7 +114,6 @@ def get_vminfo(self): vminfo.tenantName = hosting_env.deployment_name vminfo.roleName = hosting_env.role_name vminfo.roleInstanceName = goal_state.role_instance_id - vminfo.containerId = goal_state.container_id return vminfo def get_certs(self): @@ -1352,6 +1353,7 @@ def parse(self, xml_text): self.role_config_name = findtext(role_config, "ConfigName") container = find(xml_doc, "Container") self.container_id = findtext(container, "ContainerId") + os.environ[CONTAINER_ID_ENV_VARIABLE] = self.container_id self.remote_access_uri = findtext(container, "RemoteAccessInfo") lbprobe_ports = find(xml_doc, "LBProbePorts") self.load_balancer_probe_port = findtext(lbprobe_ports, "Port") diff --git a/azurelinuxagent/ga/monitor.py b/azurelinuxagent/ga/monitor.py index a69b79461d..1d63d12ef8 100644 --- a/azurelinuxagent/ga/monitor.py +++ b/azurelinuxagent/ga/monitor.py @@ -188,8 +188,6 @@ def init_sysinfo(self): vminfo.roleName)) self.sysinfo.append(TelemetryEventParam("RoleInstanceName", vminfo.roleInstanceName)) - self.sysinfo.append(TelemetryEventParam("ContainerId", - vminfo.containerId)) except ProtocolError as e: logger.warn("Failed to get system info: {0}", ustr(e)) diff --git a/tests/common/test_event.py b/tests/common/test_event.py index 615cb51c3f..ae2a3c6292 100644 --- a/tests/common/test_event.py +++ b/tests/common/test_event.py @@ -25,6 +25,7 @@ from azurelinuxagent.common.exception import EventError from azurelinuxagent.common.future import ustr from azurelinuxagent.common.utils.extensionprocessutil import read_output +from azurelinuxagent.common.protocol.wire import GoalState from azurelinuxagent.common.version import CURRENT_VERSION from azurelinuxagent.ga.monitor import MonitorHandler @@ -34,6 +35,55 @@ class TestEvent(AgentTestCase): + def test_add_event_should_read_container_id_from_process_environment(self): + tmp_file = os.path.join(self.tmp_dir, "tmp_file") + + def patch_save_event(json_data): + fileutil.write_file(tmp_file, json_data) + + with patch("azurelinuxagent.common.event.EventLogger.save_event", side_effect=patch_save_event): + # No container id is set + os.environ.pop(event.CONTAINER_ID_ENV_VARIABLE, None) + event.add_event(name='dummy_name') + data = fileutil.read_file(tmp_file) + self.assertTrue('{"name": "ContainerId", "value": "UNINITIALIZED"}' in data or + '{"value": "UNINITIALIZED", "name": "ContainerId"}' in data) + + # Container id is set as an environment variable explicitly + os.environ[event.CONTAINER_ID_ENV_VARIABLE] = '424242' + event.add_event(name='dummy_name') + data = fileutil.read_file(tmp_file) + self.assertTrue('{{"name": "ContainerId", "value": "{0}"}}'.format( + os.environ[event.CONTAINER_ID_ENV_VARIABLE]) in data or + '{{"value": "{0}", "name": "ContainerId"}}'.format( + os.environ[event.CONTAINER_ID_ENV_VARIABLE]) in data) + + # Container id is set as an environment variable when parsing the goal state + xml_text = load_data("wire/goal_state.xml") + goal_state = GoalState(xml_text) + + container_id = goal_state.container_id + event.add_event(name='dummy_name') + data = fileutil.read_file(tmp_file) + self.assertTrue('{{"name": "ContainerId", "value": "{0}"}}'.format(container_id) in data or + '{{"value": "{0}", "name": "ContainerId"}}'.format(container_id), data) + + # Container id is updated as the goal state changes, both in telemetry event and in environment variables + new_container_id = "z6d5526c-5ac2-4200-b6e2-56f2b70c5ab2" + xml_text = load_data("wire/goal_state.xml") + xml_text_updated = xml_text.replace("c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2", new_container_id) + goal_state = GoalState(xml_text_updated) + + event.add_event(name='dummy_name') + data = fileutil.read_file(tmp_file) + + # Assert both the environment variable and telemetry event got updated + self.assertEquals(os.environ[event.CONTAINER_ID_ENV_VARIABLE], new_container_id) + self.assertTrue('{{"name": "ContainerId", "value": "{0}"}}'.format(new_container_id) in data or + '{{"value": "{0}", "name": "ContainerId"}}'.format(new_container_id), data) + + os.environ.pop(event.CONTAINER_ID_ENV_VARIABLE) + def test_event_status_event_marked(self): es = event.__event_status__ @@ -239,7 +289,7 @@ def test_save_event_message_with_non_ascii_characters(self): event_str = MonitorHandler.collect_event(os.path.join(self.tmp_dir, tld_file)) event_json = json.loads(event_str) - self.assertEqual(len(event_json["parameters"]), 8) + self.assertEqual(len(event_json["parameters"]), 9) for i in event_json["parameters"]: if i["name"] == "Name": diff --git a/tests/ga/test_extension.py b/tests/ga/test_extension.py index a7beff8724..63e6276841 100644 --- a/tests/ga/test_extension.py +++ b/tests/ga/test_extension.py @@ -27,6 +27,7 @@ from azurelinuxagent.ga.exthandlers import * from azurelinuxagent.common.protocol.wire import WireProtocol, InVMArtifactsProfile +# Mock sleep to reduce test execution time SLEEP = time.sleep diff --git a/tests/ga/test_monitor.py b/tests/ga/test_monitor.py index d7cf96fc6a..60cbe35432 100644 --- a/tests/ga/test_monitor.py +++ b/tests/ga/test_monitor.py @@ -97,19 +97,16 @@ def test_add_sysinfo(self, *args): tenant_name = 'dummy_tenant' role_name = 'dummy_role' role_instance_name = 'dummy_role_instance' - container_id = 'dummy_container_id' vm_name_param = "VMName" tenant_name_param = "TenantName" role_name_param = "RoleName" role_instance_name_param = "RoleInstanceName" - container_id_param = "ContainerId" sysinfo = [TelemetryEventParam(vm_name_param, vm_name), TelemetryEventParam(tenant_name_param, tenant_name), TelemetryEventParam(role_name_param, role_name), - TelemetryEventParam(role_instance_name_param, role_instance_name), - TelemetryEventParam(container_id_param, container_id)] + TelemetryEventParam(role_instance_name_param, role_instance_name)] monitor_handler.sysinfo = sysinfo monitor_handler.add_sysinfo(event) @@ -130,11 +127,8 @@ def test_add_sysinfo(self, *args): elif p.name == role_instance_name_param: self.assertEqual(role_instance_name, p.value) counter += 1 - elif p.name == container_id_param: - self.assertEqual(container_id, p.value) - counter += 1 - self.assertEqual(5, counter) + self.assertEqual(4, counter) @patch("azurelinuxagent.ga.monitor.MonitorHandler.send_telemetry_heartbeat") @patch("azurelinuxagent.ga.monitor.MonitorHandler.collect_and_send_events")