diff --git a/src/azure-cli/azure/cli/command_modules/mysql/_params.py b/src/azure-cli/azure/cli/command_modules/mysql/_params.py index a253fa274ca..1346ee93374 100644 --- a/src/azure-cli/azure/cli/command_modules/mysql/_params.py +++ b/src/azure-cli/azure/cli/command_modules/mysql/_params.py @@ -13,7 +13,7 @@ from azure.cli.command_modules.mysql._validators import public_access_validator, maintenance_window_validator, ip_address_validator, \ firewall_rule_name_validator, validate_identity, validate_byok_identity, validate_identities, validate_action_name, validate_branch from azure.cli.core.local_context import LocalContextAttribute, LocalContextAction -from ._util import get_current_time +from ._util import get_current_time, normalize_mysql_tier from argcomplete.completers import FilesCompleter @@ -70,6 +70,7 @@ def load_arguments(self, _): # pylint: disable=too-many-statements, too-many- ) tier_arg_type = CLIArgumentType( + type=normalize_mysql_tier, options_list=['--tier'], help='Compute tier of the server. Accepted values: Burstable, GeneralPurpose, MemoryOptimized ' ) @@ -119,7 +120,8 @@ def load_arguments(self, _): # pylint: disable=too-many-statements, too-many- accelerated_logs_arg_type = CLIArgumentType( arg_type=get_enum_type(['Enabled', 'Disabled']), options_list=['--accelerated-logs'], - help='Enable or disable accelerated logs. Only support for Business Critical tier. Default value is Enabled.' + help='Enable or disable accelerated logs. Supported for General Purpose and Memory Optimized tiers. ' + 'For server creation, defaults to Enabled for Memory Optimized and Disabled for General Purpose.' ) faster_restore_arg_type = CLIArgumentType( diff --git a/src/azure-cli/azure/cli/command_modules/mysql/_util.py b/src/azure-cli/azure/cli/command_modules/mysql/_util.py index 7fe3f1ca8c5..dbe4ae30e66 100644 --- a/src/azure-cli/azure/cli/command_modules/mysql/_util.py +++ b/src/azure-cli/azure/cli/command_modules/mysql/_util.py @@ -224,6 +224,10 @@ def get_mysql_tiers(sku_info): return list(sku_info.keys()) +def normalize_mysql_tier(tier): + return 'MemoryOptimized' if tier == 'BusinessCritical' else tier + + def get_mysql_list_skus_info(cmd, location, server_name=None): list_skus_client = cf_mysql_flexible_location_capabilities(cmd.cli_ctx, '_') params = {'serverName': server_name} if server_name else None @@ -477,16 +481,6 @@ def get_user_confirmation(message, yes=False): 'Unable to prompt for confirmation as no tty available. Use --yes.') -def replace_memory_optimized_tier(result): - result = _get_list_from_paged_response(result) - for capability in result: - for edition_idx, edition in enumerate(capability.supported_flexible_server_editions): - if edition.name == 'MemoryOptimized': - capability.supported_flexible_server_editions[edition_idx].name = 'BusinessCritical' - - return result - - def _is_resource_name(resource): if len(resource.split('/')) == 1: return True diff --git a/src/azure-cli/azure/cli/command_modules/mysql/_validators.py b/src/azure-cli/azure/cli/command_modules/mysql/_validators.py index 824abe205de..ca4c599aa55 100644 --- a/src/azure-cli/azure/cli/command_modules/mysql/_validators.py +++ b/src/azure-cli/azure/cli/command_modules/mysql/_validators.py @@ -214,7 +214,7 @@ def mysql_sku_name_validator(sku_name, sku_info, tier, instance): if sku_name not in skus: raise CLIError('Incorrect value for --sku-name. The SKU name does not match tier selection. ' 'Default value for --tier is Burstable. ' - 'For Business Critical and General Purpose you need to specify --tier value explicitly. ' + 'For Memory Optimized and General Purpose you need to specify --tier value explicitly. ' 'Allowed values for given tier: {}'.format(skus)) diff --git a/src/azure-cli/azure/cli/command_modules/mysql/custom.py b/src/azure-cli/azure/cli/command_modules/mysql/custom.py index 4b00fa295fd..6425089aba2 100644 --- a/src/azure-cli/azure/cli/command_modules/mysql/custom.py +++ b/src/azure-cli/azure/cli/command_modules/mysql/custom.py @@ -26,7 +26,7 @@ cf_mysql_flexible_servers, cf_mysql_flexible_replica, cf_mysql_flexible_adadmin, cf_mysql_flexible_private_dns_zone_suffix_operations, cf_mysql_servers, \ cf_mysql_firewall_rules, get_mysql_flexible_management_client_by_sub from ._util import resolve_poller, generate_missing_parameters, get_mysql_list_skus_info, generate_password, parse_maintenance_window, \ - replace_memory_optimized_tier, build_identity_and_data_encryption, get_identity_and_data_encryption, get_tenant_id, run_subprocess, \ + _get_list_from_paged_response, build_identity_and_data_encryption, get_identity_and_data_encryption, get_tenant_id, run_subprocess, \ fill_action_template, get_git_root_dir, get_single_to_flex_sku_mapping, get_firewall_rules_from_paged_response, \ ImportFromStorageProgressHook, OperationProgressBar, GITHUB_ACTION_PATH from ._network import prepare_mysql_exist_private_dns_zone, prepare_mysql_exist_private_network, prepare_private_network, prepare_private_dns_zone, prepare_public_network @@ -360,9 +360,6 @@ def flexible_server_create(cmd, client, # Process parameters server_name = server_name.lower() - # MySQL chnged MemoryOptimized tier to BusinessCritical (only in client tool not in list-skus return) - if tier == 'BusinessCritical': - tier = 'MemoryOptimized' mysql_arguments_validator(db_context, server_name=server_name, location=location, @@ -567,9 +564,6 @@ def flexible_server_import_create(cmd, client, # Process parameters server_name = server_name.lower() - # MySQL changed MemoryOptimized tier to BusinessCritical (only in client tool not in list-skus return) - if tier == 'BusinessCritical': - tier = 'MemoryOptimized' mysql_arguments_validator(db_context, data_source_type=data_source_type, mode=mode, @@ -1016,9 +1010,6 @@ def flexible_server_update_custom_func(cmd, client, instance, sku_name=None, tie cf_availability_without_location=cf_mysql_check_resource_availability_without_location, logging_name='MySQL', command_group='mysql', server_client=client, location=instance.location) - # MySQL chnged MemoryOptimized tier to BusinessCritical (only in client tool not in list-skus return) - if tier == 'BusinessCritical': - tier = 'MemoryOptimized' mysql_arguments_validator(db_context, location=location, tier=tier, @@ -1467,8 +1458,7 @@ def flexible_server_mysql_get(cmd, resource_group_name, server_name): def flexible_list_skus(cmd, client, location): - result = client.list(location) - result = replace_memory_optimized_tier(result) + result = _get_list_from_paged_response(client.list(location)) logger.warning('For prices please refer to https://aka.ms/mysql-pricing') return result diff --git a/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_custom.py b/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_custom.py index fee7b2f69c4..26eeb07c81b 100644 --- a/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_custom.py +++ b/src/azure-cli/azure/cli/command_modules/mysql/tests/latest/test_mysql_custom.py @@ -3,9 +3,11 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- import unittest -from unittest.mock import patch +from unittest.mock import MagicMock, patch from ... import custom +from ..._params import load_arguments +from ..._util import normalize_mysql_tier class MysqlFlexibleServerFirewallRuleCustomTest(unittest.TestCase): @@ -34,6 +36,71 @@ def test_firewall_rule_create_uses_properties_payload(self): }, client.parameters.as_dict()) +class MysqlAcceleratedLogsCustomTest(unittest.TestCase): + + def test_accelerated_logs_tier_behavior(self): + self.assertEqual( + 'Enabled', + custom._determine_acceleratedLogs('Enabled', 'GeneralPurpose')) + self.assertEqual( + 'Disabled', + custom._determine_acceleratedLogs(None, 'GeneralPurpose')) + self.assertEqual( + 'Enabled', + custom._determine_acceleratedLogs(None, 'MemoryOptimized')) + self.assertEqual( + 'Disabled', + custom._determine_acceleratedLogs('Enabled', 'Burstable')) + + +class MysqlTierNormalizationTest(unittest.TestCase): + + def test_business_critical_is_normalized_as_legacy_alias(self): + self.assertEqual('MemoryOptimized', normalize_mysql_tier('BusinessCritical')) + self.assertEqual('MemoryOptimized', normalize_mysql_tier('MemoryOptimized')) + self.assertIsNone(normalize_mysql_tier(None)) + + def test_all_tier_arguments_use_legacy_alias_normalizer(self): + registrations = [] + loader = MagicMock() + loader.argument_context.side_effect = \ + lambda command_name: _FakeArgumentContext(command_name, registrations) + + load_arguments(loader, None) + + tier_arg_types = { + command_name: settings['arg_type'] + for command_name, argument_name, settings in registrations + if argument_name == 'tier' + } + self.assertEqual({ + 'mysql flexible-server create', + 'mysql flexible-server geo-restore', + 'mysql flexible-server import create', + 'mysql flexible-server replica create', + 'mysql flexible-server restore', + 'mysql flexible-server update' + }, set(tier_arg_types)) + for command_name, arg_type in tier_arg_types.items(): + with self.subTest(command_name=command_name): + self.assertIs(normalize_mysql_tier, arg_type.settings['type']) + + +class MysqlFlexibleServerListSkusCustomTest(unittest.TestCase): + + def test_list_skus_preserves_memory_optimized_tier(self): + capabilities = [_FakeCapability('MemoryOptimized')] + client = _FakeLocationCapabilitiesClient(capabilities) + + result = custom.flexible_list_skus(cmd=None, client=client, location='eastus') + + self.assertIs(capabilities, result) + self.assertEqual('eastus', client.location) + self.assertEqual( + 'MemoryOptimized', + result[0].supported_flexible_server_editions[0].name) + + class _FakeFirewallRulesClient: def begin_create_or_update(self, resource_group_name, server_name, firewall_rule_name, parameters): @@ -44,5 +111,46 @@ def begin_create_or_update(self, resource_group_name, server_name, firewall_rule return parameters +class _FakeCapability: + + def __init__(self, tier_name): + self.supported_flexible_server_editions = [_FakeEdition(tier_name)] + + +class _FakeEdition: + + def __init__(self, name): + self.name = name + + +class _FakeLocationCapabilitiesClient: + + def __init__(self, result): + self.result = result + self.location = None + + def list(self, location): + self.location = location + return self.result + + +class _FakeArgumentContext: + + def __init__(self, command_name, registrations): + self.command_name = command_name + self.registrations = registrations + + def __enter__(self): + return self + + def __exit__(self, *args): + return False + + def argument(self, argument_name, *args, **settings): + if args: + settings['arg_type'] = args[0] + self.registrations.append((self.command_name, argument_name, settings)) + + if __name__ == '__main__': unittest.main()