diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/_params.py b/src/azure-cli/azure/cli/command_modules/postgresql/_params.py index 1ac3157f4f4..fd20dcd4e7e 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/_params.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/_params.py @@ -201,30 +201,18 @@ def _flexible_server_params(command_group): options_list=['--vnet'], help='Name or ID of a new or existing virtual network. ' 'If you want to use a vnet from different resource group or subscription, ' - 'please provide a resource ID. The name must be between 2 to 64 characters. ' + 'provide a resource ID. The name must be between 2 to 64 characters. ' 'The name must begin with a letter or number, end with a letter, number or underscore, ' 'and may contain only letters, numbers, underscores, periods, or hyphens.' ) - - vnet_address_prefix_arg_type = CLIArgumentType( - options_list=['--address-prefixes'], - help='The IP address prefix to use when creating a new virtual network in CIDR format. ' - 'Default value is 10.0.0.0/16.' - ) - subnet_arg_type = CLIArgumentType( options_list=['--subnet'], help='Name or resource ID of a new or existing subnet. ' - 'If you want to use a subnet from different resource group or subscription, please provide resource ID instead of name. ' - 'Please note that the subnet will be delegated to flexibleServers. ' + 'If you want to use a subnet from different resource group or subscription, provide a resource ID instead of name. ' + 'Note that the subnet will be delegated to flexibleServers. ' 'After delegation, this subnet cannot be used for any other type of Azure resources.' ) - subnet_address_prefix_arg_type = CLIArgumentType( - options_list=['--subnet-prefixes'], - help='The subnet IP address prefix to use when creating a new subnet in CIDR format. Default value is 10.0.0.0/24.' - ) - zone_arg_type = CLIArgumentType( options_list=['--zone', '-z'], help='Availability zone into which to provision the resource.' @@ -280,16 +268,16 @@ def _flexible_server_params(command_group): private_dns_zone_arguments_arg_type = CLIArgumentType( options_list=['--private-dns-zone'], help='This parameter only applies for a server with private access. ' - 'The name or id of new or existing private dns zone. ' - 'You can use the private dns zone from same resource group, different resource group, or different subscription. ' - 'If you want to use a zone from different resource group or subscription, please provide resource Id. ' - 'CLI creates a new private dns zone within the same resource group as virtual network if not provided by users.' + 'The name or id of new or existing private DNS zone. ' + 'You can use the private DNS zone from same resource group, different resource group, or different subscription. ' + 'If you want to use a DNS zone from different resource group or subscription, provide its resource identifier. ' + 'CLI creates a new private DNS zone within the same resource group as virtual network if not provided by users.' ) restore_point_in_time_arg_type = CLIArgumentType( options_list=['--restore-time'], default=get_current_time(), - help='The point in time in UTC to restore from (ISO8601 format), e.g., 2017-04-26T02:10:00+00:00' + help='The point in time in UTC to restore from (ISO8601 format), e.g., 2026-03-22T18:20:22+00:00' 'The default value is set to current time.' ) @@ -420,9 +408,7 @@ def _flexible_server_params(command_group): c.argument('high_availability', arg_type=high_availability_arg_type, default="Disabled") c.argument('public_access', arg_type=public_access_create_arg_type) c.argument('vnet', arg_type=vnet_arg_type) - c.argument('vnet_address_prefix', arg_type=vnet_address_prefix_arg_type) c.argument('subnet', arg_type=subnet_arg_type) - c.argument('subnet_address_prefix', arg_type=subnet_address_prefix_arg_type) c.argument('private_dns_zone_arguments', private_dns_zone_arguments_arg_type) c.argument('zone', zone_arg_type) c.argument('tags', tags_type) @@ -440,9 +426,7 @@ def _flexible_server_params(command_group): c.argument('restore_point_in_time', arg_type=restore_point_in_time_arg_type) c.argument('source_server', arg_type=source_server_arg_type) c.argument('vnet', arg_type=vnet_arg_type) - c.argument('vnet_address_prefix', arg_type=vnet_address_prefix_arg_type) c.argument('subnet', arg_type=subnet_arg_type) - c.argument('subnet_address_prefix', arg_type=subnet_address_prefix_arg_type) c.argument('private_dns_zone_arguments', private_dns_zone_arguments_arg_type) c.argument('zone', arg_type=zone_arg_type) c.argument('yes', arg_type=yes_arg_type) @@ -458,9 +442,7 @@ def _flexible_server_params(command_group): c.argument('sku_name', arg_type=sku_name_arg_type) c.argument('source_server', arg_type=source_server_arg_type) c.argument('vnet', arg_type=vnet_arg_type) - c.argument('vnet_address_prefix', arg_type=vnet_address_prefix_arg_type) c.argument('subnet', arg_type=subnet_arg_type) - c.argument('subnet_address_prefix', arg_type=subnet_address_prefix_arg_type) c.argument('private_dns_zone_arguments', private_dns_zone_arguments_arg_type) c.argument('zone', arg_type=zone_arg_type) c.argument('yes', arg_type=yes_arg_type) @@ -476,9 +458,7 @@ def _flexible_server_params(command_group): c.argument('sku_name', arg_type=sku_name_arg_type) c.argument('source_server', arg_type=source_server_arg_type) c.argument('vnet', arg_type=vnet_arg_type) - c.argument('vnet_address_prefix', arg_type=vnet_address_prefix_arg_type) c.argument('subnet', arg_type=subnet_arg_type) - c.argument('subnet_address_prefix', arg_type=subnet_address_prefix_arg_type) c.argument('private_dns_zone_arguments', private_dns_zone_arguments_arg_type) c.argument('zone', arg_type=zone_arg_type) c.argument('yes', arg_type=yes_arg_type) @@ -619,8 +599,6 @@ def _flexible_server_params(command_group): c.argument('vnet', arg_type=vnet_arg_type) c.argument('subnet', arg_type=subnet_arg_type) c.argument('private_dns_zone_arguments', private_dns_zone_arguments_arg_type) - c.argument('vnet_address_prefix', arg_type=vnet_address_prefix_arg_type) - c.argument('subnet_address_prefix', arg_type=subnet_address_prefix_arg_type) c.argument('byok_key', arg_type=key_arg_type) c.argument('byok_identity', arg_type=identity_arg_type) c.argument('tier', arg_type=tier_arg_type) diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/commands/custom_commands.py b/src/azure-cli/azure/cli/command_modules/postgresql/commands/custom_commands.py index 7c446a48e3f..748fb7f41d2 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/commands/custom_commands.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/commands/custom_commands.py @@ -46,13 +46,12 @@ validate_and_format_restore_point_in_time, validate_citus_cluster, validate_georestore_network, + validate_private_dns_zone, validate_resource_group, validate_server_name) from .firewall_rule_commands import create_firewall_rule from .microsoft_entra_commands import _create_admin -from .network_commands import ( - flexible_server_provision_network_resource, - prepare_private_dns_zone) +from .network_commands import flexible_server_validate_network logger = get_logger(__name__) DEFAULT_DB_NAME = 'flexibleserverdb' @@ -72,7 +71,7 @@ def flexible_server_create(cmd, client, storage_gb=None, version=None, microsoft_entra_auth=None, admin_name=None, admin_id=None, admin_type=None, password_auth=None, administrator_login=None, administrator_login_password=None, - tags=None, subnet=None, subnet_address_prefix=None, vnet=None, vnet_address_prefix=None, + tags=None, vnet=None, subnet=None, private_dns_zone_arguments=None, public_access=None, high_availability=None, zonal_resiliency=None, allow_same_zone=False, zone=None, standby_availability_zone=None, @@ -152,18 +151,15 @@ def flexible_server_create(cmd, client, server_result = firewall_id = None - network, start_ip, end_ip = flexible_server_provision_network_resource(cmd=cmd, - resource_group_name=resource_group_name, - server_name=server_name, - location=location, - db_context=db_context, - private_dns_zone_arguments=private_dns_zone_arguments, - public_access=public_access, - vnet=vnet, - subnet=subnet, - vnet_address_prefix=vnet_address_prefix, - subnet_address_prefix=subnet_address_prefix, - yes=yes) + network, start_ip, end_ip = flexible_server_validate_network(cmd=cmd, + resource_group_name=resource_group_name, + server_name=server_name, + location=location, + db_context=db_context, + private_dns_zone_arguments=private_dns_zone_arguments, + public_access=public_access, + vnet=vnet, + subnet=subnet) storage = postgresql_flexibleservers.models.Storage(storage_size_gb=storage_gb, auto_grow=auto_grow, tier=performance_tier, type=storage_type, iops=iops, throughput=throughput) @@ -247,8 +243,8 @@ def _create_server(db_context, cmd, resource_group_name, server_name, tags, loca logging_name, server_client = db_context.logging_name, db_context.server_client logger.warning('Creating %s Server \'%s\' in group \'%s\'...', logging_name, server_name, resource_group_name) - logger.warning('Your server \'%s\' is using sku \'%s\' (Paid Tier). ' - 'Please refer to https://aka.ms/postgres-pricing for pricing details', server_name, sku.name) + logger.warning('Your server \'%s\' is using SKU \'%s\' (Paid Tier). ' + 'Refer to https://aka.ms/postgres-pricing for pricing details', server_name, sku.name) # Note : passing public-network-access has no effect as the accepted values are 'Enabled' and 'Disabled'. # So when you pass an IP here(from the CLI args of public_access), it ends up being ignored. @@ -329,7 +325,7 @@ def _form_response(username, sku, location, server_id, host, version, password, def flexible_server_restore(cmd, client, resource_group_name, server_name, source_server, restore_point_in_time=None, zone=None, no_wait=False, - subnet=None, subnet_address_prefix=None, vnet=None, vnet_address_prefix=None, + vnet=None, subnet=None, private_dns_zone_arguments=None, geo_redundant_backup=None, byok_identity=None, byok_key=None, backup_byok_identity=None, backup_byok_key=None, storage_type=None, yes=False): @@ -382,7 +378,7 @@ def flexible_server_restore(cmd, client, ) if source_server_object.network.public_network_access == 'Disabled' and any((vnet, subnet)): - parameters.network, _, _ = flexible_server_provision_network_resource(cmd=cmd, + parameters.network, _, _ = flexible_server_validate_network(cmd=cmd, resource_group_name=resource_group_name, server_name=server_name, location=location, @@ -391,8 +387,6 @@ def flexible_server_restore(cmd, client, public_access='Disabled', vnet=vnet, subnet=subnet, - vnet_address_prefix=vnet_address_prefix, - subnet_address_prefix=subnet_address_prefix, yes=yes) else: parameters.network = source_server_object.network @@ -473,13 +467,13 @@ def flexible_server_update_custom_func(cmd, client, instance, instance.network.public_network_access = public_access if private_dns_zone_arguments: - private_dns_zone_id = prepare_private_dns_zone(db_context, - resource_group_name, - server_name, - private_dns_zone=private_dns_zone_arguments, - subnet_id=instance.network.delegated_subnet_resource_id, - location=location, - yes=yes) + private_dns_zone_id = validate_private_dns_zone(db_context, + server_name=server_name, + private_dns_zone=private_dns_zone_arguments, + resource_group=resource_group_name, + subnet_id=instance.network.delegated_subnet_resource_id, + location=location, + yes=yes) instance.network.private_dns_zone_arm_resource_id = private_dns_zone_id _confirm_restart_server(instance, sku_name, storage_gb, yes) @@ -586,7 +580,7 @@ def flexible_server_update_custom_func(cmd, client, instance, config_client = cf_postgres_flexible_config(cmd.cli_ctx, '_') fabric_mirror_status = config_client.get(resource_group_name, server_name, 'azure.fabric_mirror_enabled') if (fabric_mirror_status and fabric_mirror_status.value.lower() == 'on'): - raise CLIError("High availability cannot be enabled while Fabric mirroring is Active. Please disable Fabric mirroring to enable high availability.") + raise CLIError("High availability cannot be enabled while Fabric mirroring is Active. Disable Fabric mirroring to enable high availability.") params.high_availability = high_availability_param @@ -608,9 +602,9 @@ def flexible_server_update_set(client, resource_group_name, server_name, paramet def _update_login(server_name, resource_group_name, auth_config, password_auth, administrator_login, administrator_login_password): if auth_config.password_auth.lower() == 'disabled' and password_auth.lower() == 'enabled': - administrator_login = administrator_login if administrator_login else prompt('Please enter administrator username for the server. Once set, it cannot be changed: ') + administrator_login = administrator_login if administrator_login else prompt('Enter name of the administrator login for the server. Once set, it cannot be changed: ') if not administrator_login: - raise CLIError('Administrator username is required for enabling password authentication.') + raise CLIError('Name of the administrator login is required for enabling password authentication.') if not administrator_login_password: administrator_login_password = generate_password(administrator_login_password) logger.warning('Make a note of password "%s". You can ' @@ -686,12 +680,12 @@ def server_list_custom_func(client, resource_group_name=None, show_cluster=None) def flexible_list_skus(cmd, client, location): result = client.list(location) - logger.warning('For prices please refer to https://aka.ms/postgres-pricing') + logger.warning('For prices refer to https://aka.ms/postgres-pricing') return result def flexible_server_georestore(cmd, client, resource_group_name, server_name, source_server, location, zone=None, - vnet=None, vnet_address_prefix=None, subnet=None, subnet_address_prefix=None, + vnet=None, subnet=None, private_dns_zone_arguments=None, geo_redundant_backup=None, no_wait=False, yes=False, byok_identity=None, byok_key=None, backup_byok_identity=None, backup_byok_key=None, restore_point_in_time=None): validate_resource_group(resource_group_name) @@ -745,7 +739,7 @@ def flexible_server_georestore(cmd, client, resource_group_name, server_name, so ) if source_server_object.network.public_network_access == 'Disabled': - parameters.network, _, _ = flexible_server_provision_network_resource(cmd=cmd, + parameters.network, _, _ = flexible_server_validate_network(cmd=cmd, resource_group_name=resource_group_name, server_name=server_name, location=location, @@ -754,8 +748,6 @@ def flexible_server_georestore(cmd, client, resource_group_name, server_name, so public_access='Disabled', vnet=vnet, subnet=subnet, - vnet_address_prefix=vnet_address_prefix, - subnet_address_prefix=subnet_address_prefix, yes=yes) parameters.backup = postgresql_flexibleservers.models.Backup(geo_redundant_backup=geo_redundant_backup) @@ -770,7 +762,7 @@ def flexible_server_georestore(cmd, client, resource_group_name, server_name, so def flexible_server_revivedropped(cmd, client, resource_group_name, server_name, source_server, location, zone=None, - vnet=None, vnet_address_prefix=None, subnet=None, subnet_address_prefix=None, + vnet=None, subnet=None, private_dns_zone_arguments=None, geo_redundant_backup=None, no_wait=False, yes=False, byok_identity=None, byok_key=None, backup_byok_identity=None, backup_byok_key=None): validate_resource_group(resource_group_name) @@ -810,9 +802,10 @@ def flexible_server_revivedropped(cmd, client, resource_group_name, server_name, storage=storage ) - if vnet is not None or vnet_address_prefix is not None or subnet is not None or \ - subnet_address_prefix is not None or private_dns_zone_arguments is not None: - parameters.network, _, _ = flexible_server_provision_network_resource(cmd=cmd, + if subnet is not None or \ + private_dns_zone_arguments is not None or \ + vnet is not None: + parameters.network, _, _ = flexible_server_validate_network(cmd=cmd, resource_group_name=resource_group_name, server_name=server_name, location=location, @@ -821,8 +814,6 @@ def flexible_server_revivedropped(cmd, client, resource_group_name, server_name, public_access='Disabled', vnet=vnet, subnet=subnet, - vnet_address_prefix=vnet_address_prefix, - subnet_address_prefix=subnet_address_prefix, yes=yes) parameters.backup = postgresql_flexibleservers.models.Backup(geo_redundant_backup=geo_redundant_backup) diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/commands/network_commands.py b/src/azure-cli/azure/cli/command_modules/postgresql/commands/network_commands.py index 13e61044a97..f6fdb14adbc 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/commands/network_commands.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/commands/network_commands.py @@ -6,52 +6,26 @@ # pylint: disable=unused-argument, line-too-long, import-outside-toplevel from azure.cli.core.azclierror import RequiredArgumentMissingError, ValidationError from azure.cli.core.commands import LongRunningOperation -from azure.cli.core.commands.client_factory import get_subscription_id -from azure.cli.core.util import CLIError, sdk_no_wait, user_confirmation +from azure.cli.core.util import CLIError, sdk_no_wait from azure.mgmt import postgresqlflexibleservers as postgresql_flexibleservers -from azure.mgmt.core.tools import ( # pylint: disable=import-error - is_valid_resource_id, - is_valid_resource_name, - parse_resource_id, - resource_id, -) -from azure.mgmt.privatedns.models import PrivateZone, SubResource, VirtualNetworkLink from knack.log import get_logger from requests import get -from .._client_factory import ( - private_dns_client_factory, - private_dns_link_client_factory, - resource_client_factory, -) -from .._config_reader import get_cloud_cluster -from ..aaz.latest.network.vnet import ( - Create as VNetCreate, - Show as VNetShow, - Update as _VNetUpdate, -) from ..aaz.latest.network.vnet.subnet import ( Create as SubnetCreate, - Show as SubnetShow, Update as SubnetUpdate, ) from ..utils._flexible_server_util import ( - _check_resource_group_existence, - _is_resource_name, - check_existence, - get_id_components, get_user_confirmation, parse_public_access_input, ) from ..utils.validators import ( validate_private_dns_zone, validate_resource_group, - validate_vnet_location, + validate_subnet, ) logger = get_logger(__name__) DELEGATION_SERVICE_NAME = "Microsoft.DBforPostgreSQL/flexibleServers" -DEFAULT_VNET_ADDRESS_PREFIX = '10.0.0.0/16' -DEFAULT_SUBNET_ADDRESS_PREFIX = '10.0.0.0/24' IP_ADDRESS_CHECKER = 'https://api.ipify.org' @@ -61,293 +35,7 @@ def flexible_server_migrate_network(client, resource_group_name, server_name, no return sdk_no_wait(no_wait, client.begin_migrate_network_mode, resource_group_name, server_name) -# pylint: disable=too-many-locals, too-many-statements, too-many-branches, import-outside-toplevel -def prepare_private_network(cmd, resource_group_name, server_name, vnet, subnet, location, delegation_service_name, vnet_address_pref, subnet_address_pref, yes): - - nw_subscription = get_subscription_id(cmd.cli_ctx) - resource_client = resource_client_factory(cmd.cli_ctx) - - # Handle vnet and subnet prefix - if (vnet_address_pref is not None and subnet_address_pref is None) or \ - (vnet_address_pref is None and subnet_address_pref is not None): - raise ValidationError("You need to provide both Vnet address prefix and Subnet address prefix.") - if vnet_address_pref is None: - vnet_address_pref = DEFAULT_VNET_ADDRESS_PREFIX - if subnet_address_pref is None: - subnet_address_pref = DEFAULT_SUBNET_ADDRESS_PREFIX - - # pylint: disable=too-many-nested-blocks - if subnet is not None and vnet is None: - if not is_valid_resource_id(subnet): - raise ValidationError("Incorrectly formed Subnet ID. If you are providing only --subnet (not --vnet), the Subnet parameter should be in resource ID format.") - if 'child_name_1' not in parse_resource_id(subnet): - raise ValidationError("Incorrectly formed Subnet ID. Check if the Subnet ID is in the right format.") - logger.warning("You have supplied a Subnet ID. Verifying its existence...") - subnet_result = process_private_network_with_id_input(cmd, subnet, nw_subscription, resource_client, server_name, location, delegation_service_name, vnet_address_pref, subnet_address_pref, yes) - elif subnet is None and vnet is not None: - if is_valid_resource_id(vnet): - logger.warning("You have supplied a Vnet ID. Verifying its existence...") - subnet_result = process_private_network_with_id_input(cmd, vnet, nw_subscription, resource_client, server_name, location, delegation_service_name, vnet_address_pref, subnet_address_pref, yes) - elif _is_resource_name(vnet) and is_valid_resource_name(vnet): - logger.warning("You have supplied a Vnet name. Verifying its existence...") - subnet_result = _create_vnet_subnet_delegation(cmd, nw_subscription, resource_client, delegation_service_name, resource_group_name, vnet, 'Subnet' + server_name, - location, server_name, vnet_address_pref, subnet_address_pref, yes) - else: - raise ValidationError("Incorrectly formed Vnet ID or Vnet name") - elif subnet is not None and vnet is not None: - if _is_resource_name(vnet) and _is_resource_name(subnet): - logger.warning("You have supplied a Vnet and Subnet name. Verifying its existence...") - - subnet_result = _create_vnet_subnet_delegation(cmd, nw_subscription, resource_client, delegation_service_name, resource_group_name, vnet, subnet, - location, server_name, vnet_address_pref, subnet_address_pref, yes) - - else: - raise ValidationError("If you pass both --vnet and --subnet, consider passing names instead of IDs. If you want to use an existing subnet, please provide the subnet Id only (not vnet Id).") - else: - return None - - return subnet_result["id"] - - -def process_private_network_with_id_input(cmd, rid, nw_subscription, resource_client, server_name, location, delegation_service_name, vnet_address_pref, subnet_address_pref, yes): - id_subscription, id_resource_group, id_vnet, id_subnet = get_id_components(rid) - nw_subscription, resource_client = _change_client_with_different_subscription(cmd, id_subscription, nw_subscription, resource_client) - _create_and_verify_resource_group(cmd, resource_client, id_resource_group, location, yes) - if id_subnet is None: - id_subnet = 'Subnet' + server_name - - return _create_vnet_subnet_delegation(cmd, nw_subscription, resource_client, delegation_service_name, id_resource_group, id_vnet, id_subnet, - location, server_name, vnet_address_pref, subnet_address_pref, yes) - - -def _change_client_with_different_subscription(cmd, subscription, nw_subscription, resource_client): - if subscription != get_subscription_id(cmd.cli_ctx): - logger.warning('The Vnet/Subnet ID provided is in different subscription from the server') - nw_subscription = subscription - resource_client = resource_client_factory(cmd.cli_ctx, subscription_id=subscription) - - return nw_subscription, resource_client - - -def _create_and_verify_resource_group(cmd, resource_client, resource_group, location, yes): - if not _check_resource_group_existence(cmd, resource_group, resource_client): - logger.warning("Provided resource group in the resource ID doesn't exist.") - user_confirmation("Do you want to create a new resource group {0}".format(resource_group), yes=yes) - resource_client.resource_groups.create_or_update(resource_group, {'location': location}) - - -def _create_vnet_subnet_delegation(cmd, nw_subscription, resource_client, delegation_service_name, resource_group, vnet_name, subnet_name, location, server_name, vnet_address_pref, subnet_address_pref, yes): - if not check_existence(resource_client, vnet_name, resource_group, 'Microsoft.Network', 'virtualNetworks'): - user_confirmation("Do you want to create a new Vnet {0} in resource group {1}".format(vnet_name, resource_group), yes=yes) - logger.warning('Creating new Vnet "%s" in resource group "%s"', vnet_name, resource_group) - poller = VNetCreate(cli_ctx=cmd.cli_ctx)(command_args={ - "name": vnet_name, - "subscription": nw_subscription, - "resource_group": resource_group, - "location": location, - "address_prefixes": [vnet_address_pref] - }) - LongRunningOperation(cmd.cli_ctx)(poller) - else: - logger.warning('Using existing Vnet "%s" in resource group "%s"', vnet_name, resource_group) - # check if vnet prefix is in address space and add if not there - vnet = VNetShow(cli_ctx=cmd.cli_ctx)(command_args={ - "name": vnet_name, - "subscription": nw_subscription, - "resource_group": resource_group - }) - # validate whether vnet location is same as server - validate_vnet_location(vnet, location) - prefixes = vnet["addressSpace"]["addressPrefixes"] - subnet_exist = check_existence(resource_client, subnet_name, resource_group, 'Microsoft.Network', 'subnets', parent_name=vnet_name, parent_type='virtualNetworks') - if not subnet_exist and vnet_address_pref not in prefixes: - logger.warning('The address prefix does not exist in the Vnet. Adding address prefix %s to Vnet %s.', vnet_address_pref, vnet_name) - - class VNetUpdate(_VNetUpdate): - def pre_instance_update(self, instance): - instance.properties.address_space.address_prefixes.append(vnet_address_pref) - - poller = VNetUpdate(cli_ctx=cmd.cli_ctx)(command_args={ - "name": vnet_name, - "subscription": nw_subscription, - "resource_group": resource_group - }) - LongRunningOperation(cmd.cli_ctx)(poller) - - return _create_subnet_delegation(cmd, nw_subscription, resource_client, delegation_service_name, resource_group, vnet_name, subnet_name, location, server_name, subnet_address_pref, yes) - - -def _create_subnet_delegation(cmd, nw_subscription, resource_client, delegation_service_name, resource_group, vnet_name, subnet_name, location, server_name, subnet_address_pref, yes): - delegation = {"name": delegation_service_name, "service_name": delegation_service_name} - - # subnet not exist - if not check_existence(resource_client, subnet_name, resource_group, 'Microsoft.Network', 'subnets', parent_name=vnet_name, parent_type='virtualNetworks'): - vnet = VNetShow(cli_ctx=cmd.cli_ctx)(command_args={ - "name": vnet_name, - "subscription": nw_subscription, - "resource_group": resource_group - }) - subnets = vnet.get("subnets", []) - for subnet in subnets: - vnet_subnet_prefixes = subnet.get("addressPrefix", "") if 'addressPrefix' in subnet else subnet.get("addressPrefixes", "") - if subnet_address_pref in vnet_subnet_prefixes: - raise ValidationError(f"The Subnet (default) prefix {subnet_address_pref} is already taken by another Subnet in the Vnet. Please provide a different prefix for --subnet-prefix parameter") - - user_confirmation("Do you want to create a new Subnet {0} in resource group {1}".format(subnet_name, resource_group), yes=yes) - logger.warning('Creating new Subnet "%s" in resource group "%s"', subnet_name, resource_group) - poller = SubnetCreate(cli_ctx=cmd.cli_ctx)(command_args={ - "name": subnet_name, - "vnet_name": vnet_name, - "subscription": nw_subscription, - "resource_group": resource_group, - "address_prefix": subnet_address_pref, - "delegated_services": [delegation] - }) - subnet = LongRunningOperation(cmd.cli_ctx)(poller) - # subnet exist - else: - subnet = SubnetShow(cli_ctx=cmd.cli_ctx)(command_args={ - "name": subnet_name, - "vnet_name": vnet_name, - "subscription": nw_subscription, - "resource_group": resource_group - }) - logger.warning('Using existing Subnet "%s" in resource group "%s"', subnet_name, resource_group) - subnet_address_prefix = subnet["addressPrefix"] if 'addressPrefix' in subnet else subnet["addressPrefixes"] - if subnet_address_pref not in (DEFAULT_SUBNET_ADDRESS_PREFIX, subnet_address_prefix): - logger.warning("The prefix of the subnet you provided does not match the --subnet-prefix value %s. Using current prefix %s", subnet_address_pref, subnet_address_prefix) - - # Add Delegation if not delegated already - if not subnet.get("delegations", None): - logger.warning('Adding "%s" delegation to the existing subnet %s.', delegation_service_name, subnet_name) - poller = SubnetUpdate(cli_ctx=cmd.cli_ctx)(command_args={ - "name": subnet_name, - "vnet_name": vnet_name, - "subscription": nw_subscription, - "resource_group": resource_group, - "delegated_services": [delegation] - }) - subnet = LongRunningOperation(cmd.cli_ctx)(poller) - else: - for delgtn in subnet["delegations"]: - if delgtn["serviceName"] != delegation_service_name: - raise CLIError("Can not use subnet with existing delegations other than {}".format( - delegation_service_name)) - - return subnet - - -def prepare_private_dns_zone(db_context, resource_group, server_name, private_dns_zone, subnet_id, location, yes): - cmd = db_context.cmd - - # Get Vnet Components - vnet_subscription, vnet_rg, vnet_name, _ = get_id_components(subnet_id) - vnet_id = resource_id(subscription=vnet_subscription, - resource_group=vnet_rg, - namespace='Microsoft.Network', - type='virtualNetworks', - name=vnet_name) - vnet = VNetShow(cli_ctx=cmd.cli_ctx)(command_args={ - "name": vnet_name, - "subscription": vnet_subscription, - "resource_group": vnet_rg - }) - - cluster = get_cloud_cluster(cmd, location.replace('/ +/g', '').lower(), vnet_subscription) - - if cluster is not None: - private_dns_zone_suffix = cluster["privateDnsZoneDomain"] - else: - dns_suffix_client = db_context.cf_private_dns_zone_suffix(cmd.cli_ctx, '_') - private_dns_zone_suffix = dns_suffix_client.get() - - # suffix should start with . - if private_dns_zone_suffix[0] != '.': - private_dns_zone_suffix = '.' + private_dns_zone_suffix - - # Process private dns zone (no input or Id input) - dns_rg = None - dns_subscription = vnet_subscription - if private_dns_zone is None: - if 'private' in private_dns_zone_suffix: - private_dns_zone = server_name + private_dns_zone_suffix - else: - private_dns_zone = server_name + '.private' + private_dns_zone_suffix - elif not _is_resource_name(private_dns_zone) and is_valid_resource_id(private_dns_zone): - dns_subscription, dns_rg, private_dns_zone, _ = get_id_components(private_dns_zone) - - validate_private_dns_zone(db_context, - server_name=server_name, - private_dns_zone=private_dns_zone, - private_dns_zone_suffix=private_dns_zone_suffix) - - server_sub_resource_client = resource_client_factory(cmd.cli_ctx, subscription_id=get_subscription_id(cmd.cli_ctx)) - vnet_sub_resource_client = resource_client_factory(cmd.cli_ctx, subscription_id=vnet_subscription) - dns_sub_resource_client = resource_client_factory(cmd.cli_ctx, subscription_id=dns_subscription) - - # check existence DNS zone and change resource group - if dns_rg is not None: - _create_and_verify_resource_group(cmd, dns_sub_resource_client, dns_rg, location, yes) - - # decide which resource group the dns zone provision - zone_exist_flag = False - if dns_rg is not None and check_existence(dns_sub_resource_client, private_dns_zone, dns_rg, 'Microsoft.Network', 'privateDnsZones'): - zone_exist_flag = True - elif dns_rg is None and check_existence(server_sub_resource_client, private_dns_zone, resource_group, 'Microsoft.Network', 'privateDnsZones'): - zone_exist_flag = True - dns_rg = resource_group - dns_subscription = get_subscription_id(cmd.cli_ctx) - elif dns_rg is None and check_existence(vnet_sub_resource_client, private_dns_zone, vnet_rg, 'Microsoft.Network', 'privateDnsZones'): - zone_exist_flag = True - dns_subscription = vnet_subscription - dns_rg = vnet_rg - elif dns_rg is None: - zone_exist_flag = False - dns_subscription = vnet_subscription - dns_rg = vnet_rg - - private_dns_client = private_dns_client_factory(cmd.cli_ctx, subscription_id=dns_subscription) - private_dns_link_client = private_dns_link_client_factory(cmd.cli_ctx, subscription_id=dns_subscription) - link = VirtualNetworkLink(location='global', virtual_network=SubResource(id=vnet["id"])) - link.registration_enabled = False - - # create DNS zone if not exist - if not zone_exist_flag: - user_confirmation("Do you want to create a new private DNS zone {0} in resource group {1}".format(private_dns_zone, dns_rg), yes=yes) - logger.warning('Creating a private dns zone %s in resource group "%s"', private_dns_zone, dns_rg) - private_zone = private_dns_client.begin_create_or_update(resource_group_name=dns_rg, - private_zone_name=private_dns_zone, - parameters=PrivateZone(location='global'), - if_none_match='*').result() - - private_dns_link_client.begin_create_or_update(resource_group_name=dns_rg, - private_zone_name=private_dns_zone, - virtual_network_link_name=vnet_name + '-link', - parameters=link, if_none_match='*').result() - else: - logger.warning('Using the existing private dns zone %s in resource group "%s"', private_dns_zone, dns_rg) - - private_zone = private_dns_client.get(resource_group_name=dns_rg, - private_zone_name=private_dns_zone) - virtual_links = private_dns_link_client.list(resource_group_name=dns_rg, - private_zone_name=private_dns_zone) - - link_exist_flag = False - for virtual_link in virtual_links: - if virtual_link.virtual_network.id == vnet_id: - link_exist_flag = True - break - - if not link_exist_flag: - private_dns_link_client.begin_create_or_update(resource_group_name=dns_rg, - private_zone_name=private_dns_zone, - virtual_network_link_name=vnet_name + '-link', - parameters=link, if_none_match='*').result() - - return private_zone.id - - -def prepare_public_network(public_access, yes): +def compute_firewall_rule_ip_ranges(public_access, yes): if public_access is None: try: # In USSec and USNat, the IP address checker is not available as the public Internet is not accessible. @@ -356,7 +44,7 @@ def prepare_public_network(public_access, yes): # HTTPSConnectionPool(host='api.ipify.org', port443): Max retries excceeded with url ip_address = get(IP_ADDRESS_CHECKER).text except Exception as ex: - raise CLIError("Unable to detect your current IP address. Please provide a valid IP address or CIDR range for --public-access parameter or set --public-access Disabled. Error: {}".format(ex)) + raise CLIError("Unable to detect your current IP address. Provide a valid IP address or CIDR range for --public-access parameter or set --public-access Disabled. Error: {}".format(ex)) logger.warning("Detected current client IP : %s", ip_address) if yes: @@ -379,39 +67,31 @@ def prepare_public_network(public_access, yes): return start_ip, end_ip -def flexible_server_provision_network_resource(cmd, resource_group_name, server_name, +def flexible_server_validate_network(cmd, resource_group_name, server_name, location, db_context, private_dns_zone_arguments=None, public_access=None, - vnet=None, subnet=None, vnet_address_prefix=None, subnet_address_prefix=None, yes=False): + vnet=None, subnet=None, yes=False): validate_resource_group(resource_group_name) start_ip = -1 end_ip = -1 network = postgresql_flexibleservers.models.Network() - if subnet is not None or vnet is not None: - subnet_id = prepare_private_network(cmd, + if subnet is not None: + subnet_id = validate_subnet(cmd, resource_group_name, - server_name, vnet=vnet, - subnet=subnet, - location=location, - delegation_service_name=DELEGATION_SERVICE_NAME, - vnet_address_pref=vnet_address_prefix, - subnet_address_pref=subnet_address_prefix, - yes=yes) - private_dns_zone_id = prepare_private_dns_zone(db_context, - resource_group_name, - server_name, - private_dns_zone=private_dns_zone_arguments, - subnet_id=subnet_id, - location=location, - yes=yes) + subnet=subnet) + private_dns_zone_id = validate_private_dns_zone(db_context, + server_name=server_name, + private_dns_zone=private_dns_zone_arguments, + subnet_id=subnet_id, + location=location) network.delegated_subnet_resource_id = subnet_id network.private_dns_zone_arm_resource_id = private_dns_zone_id - elif subnet is None and vnet is None and private_dns_zone_arguments is not None: - raise RequiredArgumentMissingError("Private DNS zone can only be used with private access setting. Use vnet or/and subnet parameters.") + elif subnet is None and private_dns_zone_arguments is not None: + raise RequiredArgumentMissingError("Private DNS zone can only be used with private access setting. Use --subnet parameter.") else: - start_ip, end_ip = prepare_public_network(public_access, yes=yes) + start_ip, end_ip = compute_firewall_rule_ip_ranges(public_access, yes=yes) if public_access is not None and str(public_access).lower() in ['disabled', 'none']: network.public_network_access = 'Disabled' else: diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/commands/replica_commands.py b/src/azure-cli/azure/cli/command_modules/postgresql/commands/replica_commands.py index 672c3fa4250..1c9e9e609b7 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/commands/replica_commands.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/commands/replica_commands.py @@ -25,13 +25,13 @@ cf_postgres_check_resource_availability, cf_postgres_flexible_private_dns_zone_suffix_operations) from .._db_context import DbContext -from .network_commands import flexible_server_provision_network_resource +from .network_commands import flexible_server_validate_network # pylint: disable=too-many-locals def flexible_replica_create(cmd, client, resource_group_name, source_server, replica_name=None, name=None, zone=None, - location=None, vnet=None, vnet_address_prefix=None, subnet=None, - subnet_address_prefix=None, private_dns_zone_arguments=None, no_wait=False, + location=None, subnet=None, vnet=None, + private_dns_zone_arguments=None, no_wait=False, byok_identity=None, byok_key=None, sku_name=None, tier=None, storage_gb=None, performance_tier=None, yes=False, tags=None): @@ -40,7 +40,7 @@ def flexible_replica_create(cmd, client, resource_group_name, source_server, rep if replica_name is None and name is None: raise RequiredArgumentMissingError('the following arguments are required: --name') if replica_name is not None and name is not None: - raise MutuallyExclusiveArgumentError('usage error: --name and --replica-name cannot be used together. Please use --name.') + raise MutuallyExclusiveArgumentError('usage error: --name and --replica-name cannot be used together. Use --name.') replica_name = replica_name.lower() if name is None else name.lower() if not is_valid_resource_id(source_server): @@ -97,18 +97,16 @@ def flexible_replica_create(cmd, client, resource_group_name, source_server, rep availability_zone=zone, create_mode="Replica") - if source_server_object.network.public_network_access == 'Disabled' and any((vnet, subnet)): - parameters.network, _, _ = flexible_server_provision_network_resource(cmd=cmd, + if source_server_object.network.public_network_access == 'Disabled' and subnet: + parameters.network, _, _ = flexible_server_validate_network(cmd=cmd, resource_group_name=resource_group_name, server_name=replica_name, location=location, db_context=db_context, private_dns_zone_arguments=private_dns_zone_arguments, public_access='Disabled', - vnet=vnet, subnet=subnet, - vnet_address_prefix=vnet_address_prefix, - subnet_address_prefix=subnet_address_prefix, + vnet=vnet, yes=yes) else: parameters.network = source_server_object.network @@ -161,9 +159,9 @@ def flexible_replica_promote(cmd, client, resource_group_name, replica_name, pro if is_citus_cluster(cmd, resource_group_name, replica_name): # some settings validation if promote_mode.lower() == 'standalone': - raise ValidationError("Standalone replica promotion on elastic cluster isn't currently supported. Please use 'switchover' instead.") + raise ValidationError("Standalone replica promotion on elastic cluster isn't currently supported. Use 'switchover' instead.") if promote_option.lower() == 'planned': - raise ValidationError("Planned replica promotion on elastic cluster isn't currently supported. Please use 'forced' instead.") + raise ValidationError("Planned replica promotion on elastic cluster isn't currently supported. Use 'forced' instead.") try: server_object = client.get(resource_group_name, replica_name) diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py b/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py index 15366ae430d..c95cd64308d 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/utils/_flexible_server_util.py @@ -309,7 +309,7 @@ def fill_action_template(cmd, database_engine, server, database_name, administra repository=repository) except HttpResponseError: raise AuthenticationError('You do not have authorization to create a service principal to run azure service in github actions. \n' - 'Please create a service principal that has access to the database server and add "AZURE_CREDENTIALS" secret to your github repository. \n' + 'Create a service principal that has access to the database server and add "AZURE_CREDENTIALS" secret to your github repository. \n' 'Follow the instruction here "aka.ms/github-actions-azure-credentials".') connection_string_name = server.name.upper().replace("-", "_") + "_" + database_name.upper().replace("-", "_") + "_" + database_engine.upper() + "_CONNECTION_STRING" diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/utils/validators.py b/src/azure-cli/azure/cli/command_modules/postgresql/utils/validators.py index 8b904c1a551..3ac5065b0c8 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/utils/validators.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/utils/validators.py @@ -23,6 +23,7 @@ parse_resource_id, resource_id) from .._client_factory import cf_postgres_check_resource_availability, cf_postgres_flexible_servers +from .._config_reader import get_cloud_cluster from ._flexible_server_location_capabilities_util import ( get_performance_tiers, get_performance_tiers_for_storage, @@ -30,6 +31,7 @@ get_postgres_server_capability_info) from ._flexible_server_util import ( _is_resource_name, + get_id_components, get_postgres_skus, get_postgres_storage_sizes, get_postgres_tiers) @@ -88,50 +90,48 @@ def password_validator(ns): try: ns.administrator_login_password = prompt_pass(msg='Admin Password: ') except NoTTYException: - raise CLIError('Please specify password in non-interactive mode.') + raise CLIError('Specify password in non-interactive mode.') def retention_validator(ns): if ns.backup_retention is not None: val = ns.backup_retention if not 7 <= int(val) <= 35: - raise CLIError('incorrect usage: --backup-retention. Range is 7 to 35 days.') + raise CLIError('Incorrect usage: --backup-retention. Range is 7 to 35 days.') def node_count_validator(ns): if ns.cluster_size is not None: val = ns.cluster_size if not 1 <= int(val) <= 10: - raise CLIError('incorrect usage: --node-count. Range is 1 to 10 for an elastic cluster.') + raise CLIError('Incorrect usage: --node-count. Range is 1 to 10 for an elastic cluster.') def db_renaming_cluster_validator(ns): if ns.database_name is not None and ns.create_cluster != 'ElasticCluster': - raise ArgumentUsageError('incorrect usage: --database-name can only be ' + raise ArgumentUsageError('Incorrect usage: --database-name can only be ' 'used when --cluster-option is set to ElasticCluster.') -# Validates if a subnet id or name have been given by the user. If subnet id is given, vnet-name should not be provided. -def validate_subnet(cmd, namespace): +def validate_subnet(cmd, resource_group_name, vnet, subnet): + if vnet is None: + if not is_valid_resource_id(subnet) or 'child_name_1' not in parse_resource_id(subnet): + raise ValidationError("Incorrectly formed Subnet ID. When --vnet is not provided, --subnet argument must be provided in resource ID format.") + return subnet - subnet = namespace.virtual_network_subnet_id - subnet_is_id = is_valid_resource_id(subnet) - vnet = namespace.vnet_name + if not _is_resource_name(vnet): + raise ValidationError("Incorrectly formed VNet name. When both --vnet and --subnet are provided, both must be resource names, not resource IDs.") + if not _is_resource_name(subnet): + raise ValidationError("Incorrectly formed Subnet name. When both --vnet and --subnet are provided, both must be resource names, not resource IDs.") - if (subnet_is_id and not vnet) or (not subnet and not vnet): - pass - elif subnet and not subnet_is_id and vnet: - namespace.virtual_network_subnet_id = resource_id( - subscription=get_subscription_id(cmd.cli_ctx), - resource_group=namespace.resource_group_name, - namespace='Microsoft.Network', - type='virtualNetworks', - name=vnet, - child_type_1='subnets', - child_name_1=subnet) - else: - raise CLIError('incorrect usage: [--subnet ID | --subnet NAME --vnet-name NAME]') - delattr(namespace, 'vnet_name') + return resource_id( + subscription=get_subscription_id(cmd.cli_ctx), + resource_group=resource_group_name, + namespace='Microsoft.Network', + type='virtualNetworks', + name=vnet, + child_type_1='subnets', + child_name_1=subnet) def validate_private_endpoint_connection_id(cmd, namespace): @@ -362,7 +362,7 @@ def _pg_high_availability_validator(high_availability, zonal_resiliency, allow_s if high_availability_enabled and zonal_resiliency_enabled: raise ArgumentUsageError("Setting both --high-availability and --zonal-resiliency is not allowed. " - "Please set only --zonal-resiliency to move forward.") + "Set only --zonal-resiliency to move forward.") if instance: tier = instance.sku.tier if tier is None else tier @@ -381,7 +381,7 @@ def _pg_high_availability_validator(high_availability, zonal_resiliency, allow_s raise ArgumentUsageError("High availability is not supported for Burstable tier") if single_az and allow_same_zone is False: raise ArgumentUsageError("This region is single availability zone. " - "To proceed, please set --allow-same-zone.") + "To proceed, set --allow-same-zone.") if standby_availability_zone: if not high_availability_zone_redundant and not zonal_resiliency_enabled: @@ -404,16 +404,16 @@ def pg_byok_validator(byok_identity, byok_key, backup_byok_identity=None, backup geo_redundant_backup=None, instance=None): if bool(byok_identity is None) ^ bool(byok_key is None): raise ArgumentUsageError("User assigned identity and keyvault key need to be provided together. " - "Please provide --identity and --key together.") + "Provide --identity and --key together.") if bool(backup_byok_identity is None) ^ bool(backup_byok_key is None): raise ArgumentUsageError("User assigned identity and keyvault key need to be provided together. " - "Please provide --backup-identity and --backup-key together.") + "Provide --backup-identity and --backup-key together.") if bool(byok_identity is not None) and bool(backup_byok_identity is not None) and \ byok_identity.lower() == backup_byok_identity.lower(): raise ArgumentUsageError("Primary user assigned identity and backup identity cannot be same. " - "Please provide different identities for --identity and --backup-identity.") + "Provide different identities for --identity and --backup-identity.") if (instance is not None) and \ not (instance.data_encryption and instance.data_encryption.type == 'AzureKeyVault') and \ @@ -427,7 +427,7 @@ def pg_byok_validator(byok_identity, byok_key, backup_byok_identity=None, backup "You cannot provide Geo-location user assigned identity and keyvault key.") else: if instance is None and (bool(byok_key is not None) ^ bool(backup_byok_key is not None)): - raise ArgumentUsageError("Please provide both primary as well as geo-back user assigned identity " + raise ArgumentUsageError("Provide both primary as well as geo-back user assigned identity " "and keyvault key to enable Data encryption for geo-redundant backup.") if instance is not None and (bool(byok_identity is None) ^ bool(backup_byok_identity is None)): primary_user_assigned_identity_id = byok_identity if byok_identity else \ @@ -436,7 +436,7 @@ def pg_byok_validator(byok_identity, byok_key, backup_byok_identity=None, backup instance.data_encryption.geo_backup_user_assigned_identity_id if primary_user_assigned_identity_id.lower() == geo_backup_user_assigned_identity_id.lower(): raise ArgumentUsageError("Primary user assigned identity and backup identity cannot be same. " - "Please provide different identities for --identity and --backup-identity.") + "Provide different identities for --identity and --backup-identity.") def _network_arg_validator(subnet, public_access): @@ -596,32 +596,53 @@ def validate_migration_runtime_server(cmd, migrationInstanceResourceId, target_r id_comps = parse_resource_id(migrationInstanceResourceId) runtime_server_resource_resource_type = id_comps['resource_type'].lower() if "flexibleservers" != runtime_server_resource_resource_type: - raise ValidationError("Migration Runtime Resource ID provided should be Flexible server.") + raise ValidationError("Migration runtime resource identifier provided should be that of a flexible server.") server_operations_client = cf_postgres_flexible_servers(cmd.cli_ctx, '_') target_server = server_operations_client.get(target_resource_group_name, target_server_name) if target_server.id.lower() == migrationInstanceResourceId.lower(): - raise ValidationError("Migration Runtime server is same as Target Flexible server. " - "Please change the values accordingly.") + raise ValidationError("Migration runtime server is same as target flexible server. " + "Change the values accordingly.") -def validate_private_dns_zone(db_context, server_name, private_dns_zone, private_dns_zone_suffix): +def validate_private_dns_zone(db_context, server_name, private_dns_zone, private_dns_zone_suffix=None, + subnet_id=None, location=None): cmd = db_context.cmd - server_endpoint = cmd.cli_ctx.cloud.suffixes.postgresql_server_endpoint - if private_dns_zone == server_name + server_endpoint: - raise ValidationError("private dns zone name cannot be same as the server's fully qualified domain name") + dns_subscription = None + dns_resource_group = None + + if subnet_id is not None: + dns_subscription, dns_resource_group, _, _ = get_id_components(subnet_id) - if private_dns_zone[-len(private_dns_zone_suffix):] != private_dns_zone_suffix: - raise ValidationError('The suffix of the private DNS zone should be "{}"'.format(private_dns_zone_suffix)) + if private_dns_zone_suffix is None: + if subnet_id is not None and location is not None: + cluster = get_cloud_cluster(cmd, location.replace('/ +/g', '').lower(), dns_subscription) + if cluster is not None: + private_dns_zone_suffix = cluster["privateDnsZoneDomain"] - if _is_resource_name(private_dns_zone) and not is_valid_resource_name(private_dns_zone) \ - or not _is_resource_name(private_dns_zone) and not is_valid_resource_id(private_dns_zone): - raise ValidationError("Check if the private dns zone name or Id is in correct format.") + if private_dns_zone_suffix is None: + dns_suffix_client = db_context.cf_private_dns_zone_suffix(cmd.cli_ctx, '_') + private_dns_zone_suffix = dns_suffix_client.get() + + if private_dns_zone_suffix[0] != '.': + private_dns_zone_suffix = '.' + private_dns_zone_suffix + + if private_dns_zone is None: + if 'private' in private_dns_zone_suffix: + private_dns_zone = server_name + private_dns_zone_suffix + else: + private_dns_zone = server_name + '.private' + private_dns_zone_suffix + elif is_valid_resource_id(private_dns_zone): + dns_subscription, dns_resource_group, private_dns_zone, _ = get_id_components(private_dns_zone) + if _is_resource_name(private_dns_zone) and not is_valid_resource_name(private_dns_zone): + raise ValidationError("Check if the private DNS zone name or identifier is in correct format.") -def validate_vnet_location(vnet, location): - if vnet["location"] != location: - raise ValidationError("The location of Vnet should be same as the location of the server") + return resource_id(subscription=dns_subscription, + resource_group=dns_resource_group, + namespace='Microsoft.Network', + type='privateDnsZones', + name=private_dns_zone) def validate_postgres_replica(cmd, tier, location, instance, sku_name, @@ -629,7 +650,7 @@ def validate_postgres_replica(cmd, tier, location, instance, sku_name, # Tier validation if tier == 'Burstable': raise ValidationError("Read replica is not supported for the Burstable pricing tier. " - "Scale up the source server to General Purpose or Memory Optimized. ") + "Scale up the source server to General Purpose or Memory Optimized.") if not list_location_capability_info: list_location_capability_info = get_postgres_location_capability_info(cmd, location) @@ -645,8 +666,8 @@ def validate_postgres_replica(cmd, tier, location, instance, sku_name, def validate_georestore_network(source_server_object, public_access, vnet, subnet, db_engine): if source_server_object.network.public_network_access == 'Disabled' and not any((public_access, vnet, subnet)): - raise ValidationError("Please specify network parameters if you are geo-restoring a private access server. " - F"Run 'az {db_engine} flexible-server geo-restore --help' command to see examples") + raise ValidationError("Specify network parameters if you are geo-restoring a private access server. " + F"Run 'az {db_engine} flexible-server geo-restore --help' command to see examples.") def validate_and_format_restore_point_in_time(restore_time): @@ -654,7 +675,7 @@ def validate_and_format_restore_point_in_time(restore_time): return parser.parse(restore_time) except: raise ValidationError("The restore point in time value has incorrect date format. " - "Please use ISO format e.g., 2024-10-22T00:08:23+00:00.") + "Use ISO format e.g., 2026-03-22T18:20:22Z+00:00.") def is_citus_cluster(cmd, resource_group_name, server_name): @@ -718,12 +739,12 @@ def _pg_storage_type_validator(storage_type, auto_grow, performance_tier, tier, if is_create_ssdv2: if supported_storageV2_size is None: - raise CLIError('Storage type set to PremiumV2_LRS is not supported for this region.') + raise CLIError('Storage type set to PremiumV2_LRS is not supported for this location.') if iops is None or throughput is None: raise CLIError('To set --storage-type, required to provide --iops and --throughput.') elif instance is None and (throughput is not None or iops is not None): raise CLIError('To provide values for both --iops and --throughput, ' - 'please set "--storage-type" to "PremiumV2_LRS".') + 'set "--storage-type" to "PremiumV2_LRS".') if is_create_ssdv2 or is_update_ssdv2: if auto_grow and auto_grow.lower() != 'disabled': @@ -736,7 +757,7 @@ def _pg_storage_type_validator(storage_type, auto_grow, performance_tier, tier, if throughput is not None: raise CLIError('Updating throughput is only capable for server created with Premium SSD v2.') if iops is not None: - raise CLIError('Updating storage iops is only capable for server created with Premium SSD v2.') + raise CLIError('Updating storage IOPS is only capable for server created with Premium SSD v2.') def pg_restore_validator(compute_tier, **args): @@ -750,15 +771,15 @@ def _pg_authentication_validator(password_auth, is_microsoft_entra_auth_enabled, admin_name, admin_id, admin_type, instance): if instance is None: if (password_auth is not None and password_auth.lower() == 'disabled') and not is_microsoft_entra_auth_enabled: - raise CLIError('Need to have an authentication method enabled, please set --microsoft-entra-auth ' + raise CLIError('Need to have an authentication method enabled. Set --microsoft-entra-auth ' 'to "Enabled" or --password-auth to "Enabled".') if not is_microsoft_entra_auth_enabled and (admin_name or admin_id or admin_type): - raise CLIError('To provide values for --admin-object-id, --admin-display-name, and --admin-type ' - 'please set --microsoft-entra-auth to "Enabled".') + raise CLIError('To provide values for --admin-object-id, --admin-display-name, and --admin-type, ' + 'set --microsoft-entra-auth to "Enabled".') if (admin_name is not None or admin_id is not None or admin_type is not None) and \ not (admin_name is not None and admin_id is not None and admin_type is not None): - raise CLIError('To add Microsoft Entra admin, please provide values for --admin-object-id, ' + raise CLIError('To add Microsoft Entra admin, provide values for --admin-object-id, ' '--admin-display-name, and --admin-type.')