From 7254c48f5025a52d4dd46ec9089595f698b83a12 Mon Sep 17 00:00:00 2001 From: carlotaarvela Date: Mon, 26 Jan 2026 20:00:38 +0000 Subject: [PATCH 1/3] Add container_network_logs flag --- .../azure/cli/command_modules/acs/_help.py | 13 +- .../azure/cli/command_modules/acs/_params.py | 3 + .../command_modules/acs/addonconfiguration.py | 1 + .../azure/cli/command_modules/acs/custom.py | 11 +- .../command_modules/acs/linter_exclusions.yml | 9 + .../acs/managed_cluster_decorator.py | 133 ++++++++-- .../acs/tests/latest/test_aks_commands.py | 51 +++- .../latest/test_managed_cluster_decorator.py | 244 ++++++++++++++++++ 8 files changed, 443 insertions(+), 22 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/acs/_help.py b/src/azure-cli/azure/cli/command_modules/acs/_help.py index 092b9eb8d52..97c939b9b87 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/_help.py +++ b/src/azure-cli/azure/cli/command_modules/acs/_help.py @@ -329,7 +329,7 @@ short-summary: Resource ID of Azure Monitor Private Link scope for Monitoring Addon. - name: --enable-high-log-scale-mode type: bool - short-summary: Enable High Log Scale Mode for Container Logs. + short-summary: Enable High Log Scale Mode for Container Logs. Auto-enabled when --enable-container-network-logs is specified. - name: --sku type: string short-summary: Specify SKU name for managed clusters. Use '--sku base' enables a base managed cluster. Use '--sku automatic' enables an automatic managed cluster. @@ -588,6 +588,9 @@ - name: --acns-advanced-networkpolicies type: string short-summary: Enable advanced network policies (None, FQDN or L7) on a cluster when enabling advanced networking features with "--enable-acns". + - name: --enable-container-network-logs + type: bool + short-summary: Enable container network log collection functionalities on a cluster. Automatically enables --enable-high-log-scale-mode. - name: --nrg-lockdown-restriction-level type: string short-summary: Restriction level on the managed node resource group. @@ -1089,6 +1092,12 @@ - name: --acns-advanced-networkpolicies type: string short-summary: Enable advanced network policies (None, FQDN or L7) on a cluster when enabling advanced networking features with "--enable-acns". + - name: --enable-container-network-logs + type: bool + short-summary: Enable container network log collection functionalities on a cluster. Automatically enables --enable-high-log-scale-mode. + - name: --disable-container-network-logs + type: bool + short-summary: Disable container network log collection functionalities on a cluster. - name: --nrg-lockdown-restriction-level type: string short-summary: Restriction level on the managed node resource group. @@ -1261,7 +1270,7 @@ short-summary: Resource ID of Azure Monitor Private Link scope for Monitoring Addon. - name: --enable-high-log-scale-mode type: bool - short-summary: Enable High Log Scale Mode for Container Logs. + short-summary: Enable High Log Scale Mode for Container Logs. Auto-enabled when --enable-container-network-logs is specified. - name: --appgw-name type: string short-summary: Name of the application gateway to create/use in the node resource group. Use with ingress-azure addon. diff --git a/src/azure-cli/azure/cli/command_modules/acs/_params.py b/src/azure-cli/azure/cli/command_modules/acs/_params.py index 6e47f7ddb3d..2d430cf59bb 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/_params.py +++ b/src/azure-cli/azure/cli/command_modules/acs/_params.py @@ -604,6 +604,7 @@ def load_arguments(self, _): c.argument('disable_acns_observability', action='store_true') c.argument('disable_acns_security', action='store_true') c.argument("acns_advanced_networkpolicies", arg_type=get_enum_type(advanced_networkpolicies)) + c.argument('enable_container_network_logs', action='store_true') c.argument("if_match") c.argument("if_none_match") # node provisioning @@ -661,6 +662,8 @@ def load_arguments(self, _): c.argument('disable_acns_observability', action='store_true') c.argument('disable_acns_security', action='store_true') c.argument("acns_advanced_networkpolicies", arg_type=get_enum_type(advanced_networkpolicies)) + c.argument('enable_container_network_logs', action='store_true') + c.argument('disable_container_network_logs', action='store_true') # private cluster parameters c.argument('enable_apiserver_vnet_integration', action='store_true') c.argument('apiserver_subnet_id', validator=validate_apiserver_subnet_id) diff --git a/src/azure-cli/azure/cli/command_modules/acs/addonconfiguration.py b/src/azure-cli/azure/cli/command_modules/acs/addonconfiguration.py index f98358c3e04..9124c34e989 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/addonconfiguration.py +++ b/src/azure-cli/azure/cli/command_modules/acs/addonconfiguration.py @@ -198,6 +198,7 @@ "Microsoft-ContainerInventory", "Microsoft-ContainerNodeInventory", "Microsoft-Perf", + "Microsoft-ContainerNetworkLogs", ] diff --git a/src/azure-cli/azure/cli/command_modules/acs/custom.py b/src/azure-cli/azure/cli/command_modules/acs/custom.py index 0abc58c38ec..386e606f6f8 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/custom.py +++ b/src/azure-cli/azure/cli/command_modules/acs/custom.py @@ -934,6 +934,7 @@ def aks_create( disable_acns_observability=None, disable_acns_security=None, acns_advanced_networkpolicies=None, + enable_container_network_logs=None, # network isoalted cluster bootstrap_artifact_source=CONST_ARTIFACT_SOURCE_DIRECT, bootstrap_container_registry_resource_id=None, @@ -941,10 +942,10 @@ def aks_create( enable_addons=None, workspace_resource_id=None, enable_msi_auth_for_monitoring=True, - enable_syslog=False, + enable_syslog=None, data_collection_settings=None, ampls_resource_id=None, - enable_high_log_scale_mode=False, + enable_high_log_scale_mode=None, aci_subnet_name=None, appgw_name=None, appgw_subnet_cidr=None, @@ -1161,6 +1162,8 @@ def aks_update( disable_acns_observability=None, disable_acns_security=None, acns_advanced_networkpolicies=None, + enable_container_network_logs=None, + disable_container_network_logs=None, # network isoalted cluster bootstrap_artifact_source=None, bootstrap_container_registry_resource_id=None, @@ -1557,10 +1560,10 @@ def aks_enable_addons(cmd, client, resource_group_name, name, addons, enable_secret_rotation=False, rotation_poll_interval=None, enable_msi_auth_for_monitoring=True, - enable_syslog=False, + enable_syslog=None, data_collection_settings=None, ampls_resource_id=None, - enable_high_log_scale_mode=False, + enable_high_log_scale_mode=None, no_wait=False,): instance = client.get(resource_group_name, name) msi_auth = False diff --git a/src/azure-cli/azure/cli/command_modules/acs/linter_exclusions.yml b/src/azure-cli/azure/cli/command_modules/acs/linter_exclusions.yml index 7f36cceffb4..cbe8508c5cf 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/linter_exclusions.yml +++ b/src/azure-cli/azure/cli/command_modules/acs/linter_exclusions.yml @@ -91,6 +91,9 @@ aks create: enable_static_egress_gateway: rule_exclusions: - option_length_too_long + enable_container_network_logs: + rule_exclusions: + - option_length_too_long aks enable-addons: parameters: appgw_watch_namespace: @@ -209,6 +212,12 @@ aks update: disable_static_egress_gateway: rule_exclusions: - option_length_too_long + enable_container_network_logs: + rule_exclusions: + - option_length_too_long + disable_container_network_logs: + rule_exclusions: + - option_length_too_long aks nodepool add: parameters: disable_windows_outbound_nat: diff --git a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py index 86953e7cc4c..e18b181b281 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py @@ -2593,6 +2593,81 @@ def get_acns_advanced_networkpolicies(self) -> Union[str, None]: ) return self.raw_param.get("acns_advanced_networkpolicies") + def get_container_network_logs(self, mc: ManagedCluster) -> Union[bool, None]: + """Get the enablement of container network logs + + :return: bool or None""" + enable_cnl = ( + self.raw_param.get("enable_container_network_logs") + ) + disable_cnl = ( + self.raw_param.get("disable_container_network_logs") + ) + if enable_cnl is None and disable_cnl is None: + return None + if enable_cnl and disable_cnl: + raise MutuallyExclusiveArgumentError( + "Cannot specify --enable-container-network-logs and " + "--disable-container-network-logs at the same time." + ) + if enable_cnl: + # Check if ACNS is enabled (either via parameter or already on the cluster) + acns_enabled = ( + self.raw_param.get("enable_acns", False) or + (mc.network_profile and mc.network_profile.advanced_networking and + mc.network_profile.advanced_networking.enabled) + ) + # Check if monitoring is enabled (either via parameter or already on the cluster) + enable_addons = self.raw_param.get("enable_addons", "") + monitoring_being_enabled = "monitoring" in enable_addons if enable_addons else False + monitoring_already_enabled = ( + mc.addon_profiles and + mc.addon_profiles.get("omsagent") and + mc.addon_profiles["omsagent"].enabled + ) + monitoring_enabled = monitoring_being_enabled or monitoring_already_enabled + + if not acns_enabled or not monitoring_enabled: + raise InvalidArgumentValueError( + "Container network logs requires '--enable-acns', advanced networking " + "to be enabled, and the monitoring addon to be enabled." + ) + enable_cnl = bool(enable_cnl) if enable_cnl is not None else False + disable_cnl = bool(disable_cnl) if disable_cnl is not None else False + return enable_cnl or not disable_cnl + + def get_enable_high_log_scale_mode(self) -> Union[bool, None]: + """Obtain the value of enable_high_log_scale_mode. + + This method automatically enables high log scale mode when container network logs are enabled. + It validates that the user has not explicitly disabled high log scale mode when CNL is enabled. + + Note: ACNS and monitoring addon validation is handled in get_container_network_logs(). + + :return: bool or None + """ + # Read the original value passed by the command + enable_high_log_scale_mode = self.raw_param.get("enable_high_log_scale_mode") + + # Check if container network logs are being enabled + enable_container_network_logs = self.raw_param.get("enable_container_network_logs") + + # If container network logs are being enabled, auto-enable high log scale mode + if enable_container_network_logs: + # If user explicitly set enable_high_log_scale_mode to False, raise an error + if enable_high_log_scale_mode is False: + raise MutuallyExclusiveArgumentError( + "Cannot explicitly disable --enable-high-log-scale-mode when " + "--enable-container-network-logs is specified. Container network logs " + "requires high log scale mode to be enabled." + ) + + # Auto-enable high log scale mode + return True + + # If container network logs are not being enabled, return the original value + return enable_high_log_scale_mode + def _get_pod_cidr_and_service_cidr_and_dns_service_ip_and_docker_bridge_address_and_network_policy( self, enable_validation: bool = False ) -> Tuple[ @@ -3025,21 +3100,6 @@ def get_enable_syslog(self) -> Union[bool, None]: # this parameter does not need validation return enable_syslog - def get_enable_high_log_scale_mode(self) -> Union[bool, None]: - """Obtain the value of enable_high_log_scale_mode. - - Note: The arg type of this parameter supports three states (True, False or None), but the corresponding default - value in entry function is not None. - - :return: bool or None - """ - # read the original value passed by the command - enable_high_log_scale_mode = self.raw_param.get("enable_high_log_scale_mode") - - # this parameter does not need dynamic completion - # this parameter does not need validation - return enable_high_log_scale_mode - def get_data_collection_settings(self) -> Union[str, None]: """Obtain the value of data_collection_settings. @@ -6657,6 +6717,22 @@ def set_up_addon_profiles(self, mc: ManagedCluster) -> ManagedCluster: addon_profiles[ CONST_AZURE_KEYVAULT_SECRETS_PROVIDER_ADDON_NAME ] = self.build_azure_keyvault_secrets_provider_addon_profile() + + # Set up container network logs if enabled + container_network_logs_enabled = self.context.get_container_network_logs(mc) + if container_network_logs_enabled is not None: + monitoring_addon_profile = addon_profiles.get(CONST_MONITORING_ADDON_NAME) + if monitoring_addon_profile: + config = monitoring_addon_profile.config or {} + config["enableRetinaNetworkFlags"] = str(container_network_logs_enabled) + monitoring_addon_profile.config = config + + # Trigger validation for high log scale mode when container network logs are enabled. + # This ensures proper error messages are raised before cluster creation if the user + # explicitly disables high log scale mode while enabling container network logs. + if self.context.raw_param.get("enable_container_network_logs"): + self.context.get_enable_high_log_scale_mode() + mc.addon_profiles = addon_profiles return mc @@ -8233,6 +8309,31 @@ def update_network_profile_advanced_networking(self, mc: ManagedCluster) -> Mana mc.network_profile.advanced_networking = acns return mc + def update_monitoring_profile_flow_logs(self, mc: ManagedCluster) -> ManagedCluster: + """Update monitor profile for the ManagedCluster object for flow logs. + + :return: the ManagedCluster object + """ + self._ensure_mc(mc) + + # Trigger validation for high log scale mode when container network logs are enabled. + # This ensures proper error messages are raised before cluster update if the user + # explicitly disables high log scale mode while enabling container network logs. + if self.context.raw_param.get("enable_container_network_logs"): + self.context.get_enable_high_log_scale_mode() + + container_network_logs_enabled = self.context.get_container_network_logs(mc) + if container_network_logs_enabled is not None: + if mc.addon_profiles: + addon_consts = self.context.get_addon_consts() + CONST_MONITORING_ADDON_NAME = addon_consts.get("CONST_MONITORING_ADDON_NAME") + monitoring_addon_profile = mc.addon_profiles.get(CONST_MONITORING_ADDON_NAME) + if monitoring_addon_profile: + config = monitoring_addon_profile.config or {} + config["enableRetinaNetworkFlags"] = str(container_network_logs_enabled) + mc.addon_profiles[CONST_MONITORING_ADDON_NAME].config = config + return mc + def update_http_proxy_config(self, mc: ManagedCluster) -> ManagedCluster: """Set up http proxy config for the ManagedCluster object. @@ -9518,6 +9619,8 @@ def update_mc_profile_default(self) -> ManagedCluster: mc = self.update_network_profile(mc) # update network profile with acns mc = self.update_network_profile_advanced_networking(mc) + # update monitoring profile flow logs + mc = self.update_monitoring_profile_flow_logs(mc) # update aad profile mc = self.update_aad_profile(mc) # update oidc issuer profile diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py index 21f6f7b97ec..5f3a81b6556 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_aks_commands.py @@ -13162,7 +13162,56 @@ def test_aks_create_with_enable_acns_complex( "aks delete -g {resource_group} -n {name} --yes --no-wait", checks=[self.is_empty()], ) - + + @live_only() + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer( + random_name_length=17, + name_prefix="clitest", + location="eastus2euap", + ) + def test_aks_create_acns_with_flow_logs( + self, resource_group, resource_group_location + ): + # reset the count so in replay mode the random names will start with 0 + self.test_resources_count = 0 + # kwargs for string formatting + aks_name = self.create_random_name("cliakstest", 16) + self.kwargs.update( + { + "resource_group": resource_group, + "name": aks_name, + "location": resource_group_location, + "resource_type": "Microsoft.ContainerService/ManagedClusters", + "ssh_key_value": self.generate_ssh_keys(), + } + ) + + # create: enable acns with enable container network logs and enable high log scale mode + create_cmd = ( + "aks create --resource-group={resource_group} --name={name} --location={location} " + "--ssh-key-value={ssh_key_value} --node-count=1 --tier standard " + "--network-plugin azure --network-dataplane=cilium --network-plugin-mode overlay " + "--enable-acns " + "--enable-container-network-logs " + "--enable-addons monitoring " + "--enable-high-log-scale-mode " + "--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AdvancedNetworkingPreview " + ) + self.cmd( + create_cmd, + checks=[ + self.check("provisioningState", "Succeeded"), + self.check("networkProfile.advancedNetworking.observability.enabled", True), + ], + ) + + # delete + self.cmd( + "aks delete -g {resource_group} -n {name} --yes --no-wait", + checks=[self.is_empty()], + ) + @AllowLargeResponse() @AKSCustomResourceGroupPreparer( random_name_length=17, diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py index 62d6225cbd2..5283805f6f0 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py @@ -14142,6 +14142,250 @@ def test_update_node_provisioning_profile(self): ) self.assertEqual(dec_mc_2, ground_truth_mc_2) + def test_enable_container_network_logs(self): + # Case 1: enable_acns, enable monitoring addons_profile, enable container_network_logs + dec_1 = AKSManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "enable_container_network_logs": True, + }, + ResourceType.MGMT_CONTAINERSERVICE, + ) + mc_1 = self.models.ManagedCluster( + location="test_location", + network_profile=self.models.ContainerServiceNetworkProfile( + network_plugin="azure", + network_plugin_mode="overlay", + network_dataplane="cilium", + pod_cidr="100.64.0.0/16", + service_cidr="192.168.0.0/16", + advanced_networking=self.models.AdvancedNetworking( + enabled=True, + ), + ), + addon_profiles={ + "omsagent": self.models.ManagedClusterAddonProfile( + enabled=True, + ) + }, + ) + dec_1.context.attach_mc(mc_1) + dec_mc_1 = dec_1.update_monitoring_profile_flow_logs(mc_1) + ground_truth_mc_1 = self.models.ManagedCluster( + location="test_location", + network_profile=self.models.ContainerServiceNetworkProfile( + network_plugin="azure", + network_plugin_mode="overlay", + network_dataplane="cilium", + pod_cidr="100.64.0.0/16", + service_cidr="192.168.0.0/16", + advanced_networking=self.models.AdvancedNetworking( + enabled=True, + ), + ), + addon_profiles={ + "omsagent": self.models.ManagedClusterAddonProfile( + enabled=True, + config={"enableRetinaNetworkFlags": "True"} + ) + }, + ) + self.assertEqual(dec_mc_1, ground_truth_mc_1) + + # Case 2: acns is enabled, monitoring is enabled, disable container network logs + dec_2 = AKSManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "disable_container_network_logs": True, + }, + ResourceType.MGMT_CONTAINERSERVICE, + ) + mc_2 = self.models.ManagedCluster( + location="test_location", + network_profile=self.models.ContainerServiceNetworkProfile( + network_plugin="azure", + network_plugin_mode="overlay", + network_dataplane="cilium", + pod_cidr="100.64.0.0/16", + service_cidr="192.168.0.0/16", + advanced_networking=self.models.AdvancedNetworking( + enabled=True, + ), + ), + addon_profiles={ + "omsagent": self.models.ManagedClusterAddonProfile( + enabled=True, + config={"enableRetinaNetworkFlags": "True"} + ) + }, + ) + dec_2.context.attach_mc(mc_2) + dec_mc_2 = dec_2.update_monitoring_profile_flow_logs(mc_2) + ground_truth_mc_2 = self.models.ManagedCluster( + location="test_location", + network_profile=self.models.ContainerServiceNetworkProfile( + network_plugin="azure", + network_plugin_mode="overlay", + network_dataplane="cilium", + pod_cidr="100.64.0.0/16", + service_cidr="192.168.0.0/16", + advanced_networking=self.models.AdvancedNetworking( + enabled=True, + ), + ), + addon_profiles={ + "omsagent": self.models.ManagedClusterAddonProfile( + enabled=True, + config={"enableRetinaNetworkFlags": "False"} + ) + }, + ) + self.assertEqual(dec_mc_2, ground_truth_mc_2) + + # Case 3: enable_acns, enable_container_network_logs, but monitoring does not exist + dec_3 = AKSManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "enable_acns": True, + "enable_container_network_logs": True, + }, + ResourceType.MGMT_CONTAINERSERVICE, + ) + mc_3 = self.models.ManagedCluster( + location="test_location", + network_profile=self.models.ContainerServiceNetworkProfile( + network_plugin="azure", + network_plugin_mode="overlay", + network_dataplane="cilium", + pod_cidr="100.64.0.0/16", + service_cidr="192.168.0.0/16", + advanced_networking=self.models.AdvancedNetworking( + enabled=True, + ), + ), + ) + dec_3.context.attach_mc(mc_3) + with self.assertRaises(InvalidArgumentValueError): + dec_3.update_monitoring_profile_flow_logs(mc_3) + + # Case 4: enable_acns, enable monitoring addons_profile, enable container_network_logs for ClusterCreate context + dec_4 = AKSManagedClusterCreateDecorator( + self.cmd, + self.client, + { + "name": "test_name", + "resource_group_name": "test_rg_name", + "location": "test_location", + "vnet_subnet_id": "test_vnet_subnet_id", + "enable_addons": "monitoring", + "workspace_resource_id": "test_workspace_resource_id", + "enable_msi_auth_for_monitoring": True, + "enable_acns": True, + "enable_container_network_logs": True, + }, + ResourceType.MGMT_CONTAINERSERVICE, + ) + mc_4 = self.models.ManagedCluster( + location="test_location", + network_profile=self.models.ContainerServiceNetworkProfile( + network_plugin="azure", + network_plugin_mode="overlay", + network_dataplane="cilium", + pod_cidr="100.64.0.0/16", + service_cidr="192.168.0.0/16", + ), + ) + dec_4.context.set_intermediate("subscription_id", "test_subscription_id") + dec_4.context.attach_mc(mc_4) + with patch( + "azure.cli.command_modules.acs.managed_cluster_decorator.ensure_container_insights_for_monitoring", + return_value=None, + ): + dec_mc_4 = dec_4.set_up_addon_profiles(mc_4) + ground_truth_mc_4 = { + CONST_MONITORING_ADDON_NAME: self.models.ManagedClusterAddonProfile( + enabled=True, + config={ + CONST_MONITORING_LOG_ANALYTICS_WORKSPACE_RESOURCE_ID: "/test_workspace_resource_id", + CONST_MONITORING_USING_AAD_MSI_AUTH: "true", + "enableRetinaNetworkFlags": "True", + }, + ), + } + self.assertEqual(dec_mc_4.addon_profiles["omsagent"], ground_truth_mc_4["omsagent"]) + + # Case 5: enable_acns and enable_container_network_logs without monitoring addon + dec_5 = AKSManagedClusterCreateDecorator( + self.cmd, + self.client, + { + "name": "test_name", + "resource_group_name": "test_rg_name", + "location": "test_location", + "vnet_subnet_id": "test_vnet_subnet_id", + "enable_addons": "", + "enable_acns": True, + "enable_container_network_logs": True, + }, + ResourceType.MGMT_CONTAINERSERVICE, + ) + mc_5 = self.models.ManagedCluster( + location="test_location", + network_profile=self.models.ContainerServiceNetworkProfile( + network_plugin="azure", + network_plugin_mode="overlay", + network_dataplane="cilium", + pod_cidr="100.64.0.0/16", + service_cidr="192.168.0.0/16", + ), + ) + dec_5.context.set_intermediate("subscription_id", "test_subscription_id") + dec_5.context.attach_mc(mc_5) + with self.assertRaises(InvalidArgumentValueError): + with patch( + "azure.cli.command_modules.acs.managed_cluster_decorator.ensure_container_insights_for_monitoring", + return_value=None, + ): + dec_5.set_up_addon_profiles(mc_5) + + # Case 6: enable_monitoring addon with container_network_logs, but acns is not enabled + dec_6 = AKSManagedClusterCreateDecorator( + self.cmd, + self.client, + { + "name": "test_name", + "resource_group_name": "test_rg_name", + "location": "test_location", + "vnet_subnet_id": "test_vnet_subnet_id", + "enable_addons": "monitoring", + "workspace_resource_id": "test_workspace_resource_id", + "enable_msi_auth_for_monitoring": True, + "enable_container_network_logs": True, + }, + ResourceType.MGMT_CONTAINERSERVICE, + ) + mc_6 = self.models.ManagedCluster( + location="test_location", + network_profile=self.models.ContainerServiceNetworkProfile( + network_plugin="azure", + network_plugin_mode="overlay", + network_dataplane="cilium", + pod_cidr="100.64.0.0/16", + service_cidr="192.168.0.0/16", + ), + ) + dec_6.context.set_intermediate("subscription_id", "test_subscription_id") + dec_6.context.attach_mc(mc_6) + with self.assertRaises(InvalidArgumentValueError): + with patch( + "azure.cli.command_modules.acs.managed_cluster_decorator.ensure_container_insights_for_monitoring", + return_value=None, + ): + dec_6.set_up_addon_profiles(mc_6) + if __name__ == "__main__": unittest.main() From 878235cb2a54c5d54e6e0063660b2cd6cee9074c Mon Sep 17 00:00:00 2001 From: carlotaarvela Date: Tue, 27 Jan 2026 18:01:23 +0000 Subject: [PATCH 2/3] Fix CNL validation and unit tests --- .../acs/managed_cluster_decorator.py | 52 +++++++------- .../latest/test_managed_cluster_decorator.py | 68 +++++++++++++++++++ 2 files changed, 93 insertions(+), 27 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py index e18b181b281..306f11682b9 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py @@ -2597,12 +2597,8 @@ def get_container_network_logs(self, mc: ManagedCluster) -> Union[bool, None]: """Get the enablement of container network logs :return: bool or None""" - enable_cnl = ( - self.raw_param.get("enable_container_network_logs") - ) - disable_cnl = ( - self.raw_param.get("disable_container_network_logs") - ) + enable_cnl = self.raw_param.get("enable_container_network_logs") + disable_cnl = self.raw_param.get("disable_container_network_logs") if enable_cnl is None and disable_cnl is None: return None if enable_cnl and disable_cnl: @@ -2610,28 +2606,30 @@ def get_container_network_logs(self, mc: ManagedCluster) -> Union[bool, None]: "Cannot specify --enable-container-network-logs and " "--disable-container-network-logs at the same time." ) - if enable_cnl: - # Check if ACNS is enabled (either via parameter or already on the cluster) - acns_enabled = ( - self.raw_param.get("enable_acns", False) or - (mc.network_profile and mc.network_profile.advanced_networking and - mc.network_profile.advanced_networking.enabled) - ) - # Check if monitoring is enabled (either via parameter or already on the cluster) - enable_addons = self.raw_param.get("enable_addons", "") - monitoring_being_enabled = "monitoring" in enable_addons if enable_addons else False - monitoring_already_enabled = ( - mc.addon_profiles and - mc.addon_profiles.get("omsagent") and - mc.addon_profiles["omsagent"].enabled - ) - monitoring_enabled = monitoring_being_enabled or monitoring_already_enabled - if not acns_enabled or not monitoring_enabled: - raise InvalidArgumentValueError( - "Container network logs requires '--enable-acns', advanced networking " - "to be enabled, and the monitoring addon to be enabled." - ) + # Check if monitoring is being enabled via enable_addons parameter (for create scenarios) + enable_addons = self.raw_param.get("enable_addons") + monitoring_via_enable_addons = enable_addons and "monitoring" in enable_addons + + # Check if monitoring is already enabled on the cluster + monitoring_on_cluster = ( + mc.addon_profiles and + mc.addon_profiles.get("omsagent") and + mc.addon_profiles["omsagent"].enabled + ) + + # Check if ACNS is being enabled or already enabled + acns_enabled = ( + self.raw_param.get("enable_acns", False) or + (mc.network_profile and mc.network_profile.advanced_networking and + mc.network_profile.advanced_networking.enabled) + ) + + if enable_cnl and (not acns_enabled or not (monitoring_via_enable_addons or monitoring_on_cluster)): + raise InvalidArgumentValueError( + "Container network logs requires '--enable-acns', advanced networking " + "to be enabled, and the monitoring addon to be enabled." + ) enable_cnl = bool(enable_cnl) if enable_cnl is not None else False disable_cnl = bool(disable_cnl) if disable_cnl is not None else False return enable_cnl or not disable_cnl diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py index 5283805f6f0..f29aec6bb50 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py @@ -14386,6 +14386,74 @@ def test_enable_container_network_logs(self): ): dec_6.set_up_addon_profiles(mc_6) + # Case 7: enable_container_network_logs for update with ACNS already enabled and monitoring enabled + dec_7 = AKSManagedClusterUpdateDecorator( + self.cmd, + self.client, + { + "enable_container_network_logs": True, + }, + ResourceType.MGMT_CONTAINERSERVICE, + ) + mc_7 = self.models.ManagedCluster( + location="test_location", + network_profile=self.models.ContainerServiceNetworkProfile( + network_plugin="azure", + network_plugin_mode="overlay", + network_dataplane="azure", + pod_cidr="100.64.0.0/16", + service_cidr="192.168.0.0/16", + advanced_networking=self.models.AdvancedNetworking( + enabled=True, + ), + ), + addon_profiles={ + "omsagent": self.models.ManagedClusterAddonProfile( + enabled=True, + ) + }, + ) + dec_7.context.attach_mc(mc_7) + # Should succeed since ACNS and monitoring are enabled on cluster + dec_7.update_monitoring_profile_flow_logs(mc_7) + + # Case 8: enable_container_network_logs with enable_high_log_scale_mode explicitly set to False + dec_8 = AKSManagedClusterCreateDecorator( + self.cmd, + self.client, + { + "name": "test_name", + "resource_group_name": "test_rg_name", + "location": "test_location", + "vnet_subnet_id": "test_vnet_subnet_id", + "enable_addons": "monitoring", + "workspace_resource_id": "test_workspace_resource_id", + "enable_msi_auth_for_monitoring": True, + "enable_acns": True, + "enable_container_network_logs": True, + "enable_high_log_scale_mode": False, + }, + ResourceType.MGMT_CONTAINERSERVICE, + ) + mc_8 = self.models.ManagedCluster( + location="test_location", + network_profile=self.models.ContainerServiceNetworkProfile( + network_plugin="azure", + network_plugin_mode="overlay", + network_dataplane="cilium", + pod_cidr="100.64.0.0/16", + service_cidr="192.168.0.0/16", + ), + ) + dec_8.context.set_intermediate("subscription_id", "test_subscription_id") + dec_8.context.attach_mc(mc_8) + with self.assertRaises(MutuallyExclusiveArgumentError): + with patch( + "azure.cli.command_modules.acs.managed_cluster_decorator.ensure_container_insights_for_monitoring", + return_value=None, + ): + dec_8.set_up_addon_profiles(mc_8) + if __name__ == "__main__": unittest.main() From c45d70171017d21475eaa46a8bb12b24db329c0a Mon Sep 17 00:00:00 2001 From: carlotaarvela Date: Wed, 28 Jan 2026 18:10:19 +0000 Subject: [PATCH 3/3] Add cilium network dataplane validation for container network logs --- .../acs/managed_cluster_decorator.py | 16 +++++++++++++--- .../latest/test_managed_cluster_decorator.py | 7 ++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py index 306f11682b9..72ca57521ea 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/managed_cluster_decorator.py @@ -2625,10 +2625,20 @@ def get_container_network_logs(self, mc: ManagedCluster) -> Union[bool, None]: mc.network_profile.advanced_networking.enabled) ) - if enable_cnl and (not acns_enabled or not (monitoring_via_enable_addons or monitoring_on_cluster)): + # Check if network dataplane is set to cilium (either via parameter or already on the cluster) + network_dataplane_param = self.raw_param.get("network_dataplane") + network_dataplane_cluster = None + if mc.network_profile is not None: + network_dataplane_cluster = getattr(mc.network_profile, "network_dataplane", None) + network_dataplane = network_dataplane_param or network_dataplane_cluster + cilium_enabled = safe_lower(network_dataplane) == "cilium" + + monitoring_enabled = monitoring_via_enable_addons or monitoring_on_cluster + + if enable_cnl and (not acns_enabled or not monitoring_enabled or not cilium_enabled): raise InvalidArgumentValueError( - "Container network logs requires '--enable-acns', advanced networking " - "to be enabled, and the monitoring addon to be enabled." + "Container network logs requires ACNS to be enabled, the monitoring addon to be enabled, " + "and the cilium network dataplane." ) enable_cnl = bool(enable_cnl) if enable_cnl is not None else False disable_cnl = bool(disable_cnl) if disable_cnl is not None else False diff --git a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py index f29aec6bb50..e9ef4ef523e 100644 --- a/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py +++ b/src/azure-cli/azure/cli/command_modules/acs/tests/latest/test_managed_cluster_decorator.py @@ -14386,7 +14386,7 @@ def test_enable_container_network_logs(self): ): dec_6.set_up_addon_profiles(mc_6) - # Case 7: enable_container_network_logs for update with ACNS already enabled and monitoring enabled + # Case 7: enable_container_network_logs for update with ACNS enabled, monitoring enabled, but NOT cilium dec_7 = AKSManagedClusterUpdateDecorator( self.cmd, self.client, @@ -14414,8 +14414,9 @@ def test_enable_container_network_logs(self): }, ) dec_7.context.attach_mc(mc_7) - # Should succeed since ACNS and monitoring are enabled on cluster - dec_7.update_monitoring_profile_flow_logs(mc_7) + # Should fail since cilium network dataplane is required + with self.assertRaises(InvalidArgumentValueError): + dec_7.update_monitoring_profile_flow_logs(mc_7) # Case 8: enable_container_network_logs with enable_high_log_scale_mode explicitly set to False dec_8 = AKSManagedClusterCreateDecorator(