|
13 | 13 | CLIError, |
14 | 14 | sdk_no_wait, |
15 | 15 | ) |
| 16 | +from azure.cli.core.azclierror import ( |
| 17 | + ResourceNotFoundError, |
| 18 | + ValidationError, |
| 19 | + AzCLIError |
| 20 | +) |
16 | 21 |
|
17 | 22 | from azure.mgmt.sql.models import ( |
18 | 23 | AdministratorName, |
|
58 | 63 | SensitivityLabelSource, |
59 | 64 | ServerAzureADOnlyAuthentication, |
60 | 65 | ServerConnectionPolicy, |
| 66 | + ServerCreateMode, |
61 | 67 | ServerExternalAdministrator, |
62 | 68 | ServerInfo, |
63 | 69 | ServerKey, |
|
92 | 98 | get_sql_replication_links_operations, |
93 | 99 | get_sql_elastic_pools_operations, |
94 | 100 | get_sql_databases_operations, |
| 101 | + get_sql_deleted_servers_operations, |
95 | 102 | ) |
96 | 103 |
|
97 | 104 |
|
@@ -179,6 +186,58 @@ def _is_serverless_slo(sku_name): |
179 | 186 | return "_S_" in sku_name |
180 | 187 |
|
181 | 188 |
|
| 189 | +def _check_server_exists(client, resource_group_name, server_name): |
| 190 | + ''' |
| 191 | + Checks if a server already exists and raises ValidationError if it does. |
| 192 | + Returns False if server doesn't exist (ResourceNotFound/NotFound), |
| 193 | + raises the original exception for other errors. |
| 194 | + ''' |
| 195 | + try: |
| 196 | + existing_server = client.get(resource_group_name, server_name) |
| 197 | + if existing_server: |
| 198 | + raise ValidationError( |
| 199 | + f'Server "{server_name}" already exists in resource group "{resource_group_name}". ' |
| 200 | + 'Cannot restore to an existing server name.', |
| 201 | + 'Please specify a different server name for the restore operation.') |
| 202 | + except ValidationError: |
| 203 | + # Re-raise ValidationError as-is |
| 204 | + raise |
| 205 | + except Exception as ex: |
| 206 | + # If server doesn't exist, that's what we want - continue with restore |
| 207 | + if 'ResourceNotFound' not in str(ex) and 'NotFound' not in str(ex): |
| 208 | + # Re-raise if it's not a "not found" error |
| 209 | + raise |
| 210 | + return False |
| 211 | + |
| 212 | + |
| 213 | +def _validate_deleted_server_exists(cmd, location, server_name): |
| 214 | + """ |
| 215 | + Validates that a deleted server exists and can be restored. |
| 216 | + """ |
| 217 | + try: |
| 218 | + deleted_servers_client = get_sql_deleted_servers_operations(cmd.cli_ctx, None) |
| 219 | + deleted_server = deleted_servers_client.get(location, server_name) |
| 220 | + |
| 221 | + if deleted_server: |
| 222 | + logger.info('Found deleted server: %s', deleted_server.name) |
| 223 | + return True |
| 224 | + |
| 225 | + except Exception as ex: |
| 226 | + # If server doesn't exist in deleted servers, raise appropriate error |
| 227 | + if 'ResourceNotFound' in str(ex) or 'NotFound' in str(ex): |
| 228 | + raise ResourceNotFoundError( |
| 229 | + f'No deleted server found with name "{server_name}" in location "{location}".', |
| 230 | + 'Please verify the server was deleted with soft delete enabled ' |
| 231 | + 'and is within the retention period.') from ex |
| 232 | + # Handle any other unexpected errors |
| 233 | + raise AzCLIError(f'Failed to validate deleted server "{server_name}": {str(ex)}') from ex |
| 234 | + |
| 235 | + # This shouldn't be reached if the API call succeeds but returns None |
| 236 | + raise ResourceNotFoundError( |
| 237 | + f'No deleted server found with name "{server_name}" in location "{location}".', |
| 238 | + 'Please verify the server was deleted with soft delete enabled and is within the retention period.') |
| 239 | + |
| 240 | + |
182 | 241 | def _get_default_server_version(location_capabilities): |
183 | 242 | ''' |
184 | 243 | Gets the default server version capability from the full location |
@@ -4369,6 +4428,8 @@ def server_create( |
4369 | 4428 | external_admin_principal_type=None, |
4370 | 4429 | external_admin_sid=None, |
4371 | 4430 | external_admin_name=None, |
| 4431 | + enable_soft_delete=None, |
| 4432 | + soft_delete_retention_days=None, |
4372 | 4433 | **kwargs): |
4373 | 4434 | ''' |
4374 | 4435 | Creates a server. |
@@ -4414,13 +4475,58 @@ def server_create( |
4414 | 4475 | azure_ad_only_authentication=ad_only, |
4415 | 4476 | tenant_id=tenant_id) |
4416 | 4477 |
|
| 4478 | + kwargs['create_mode'] = ServerCreateMode.NORMAL |
| 4479 | + |
| 4480 | + if enable_soft_delete is not None: |
| 4481 | + if soft_delete_retention_days is not None: |
| 4482 | + kwargs['retention_days'] = soft_delete_retention_days |
| 4483 | + else: |
| 4484 | + kwargs['retention_days'] = 7 |
| 4485 | + |
4417 | 4486 | # Create |
4418 | 4487 | return sdk_no_wait(no_wait, client.begin_create_or_update, |
4419 | 4488 | server_name=server_name, |
4420 | 4489 | resource_group_name=resource_group_name, |
4421 | 4490 | parameters=kwargs) |
4422 | 4491 |
|
4423 | 4492 |
|
| 4493 | +def server_restore( |
| 4494 | + cmd, |
| 4495 | + client, |
| 4496 | + resource_group_name, |
| 4497 | + server_name, |
| 4498 | + location, |
| 4499 | + no_wait=False, |
| 4500 | + **kwargs): |
| 4501 | + ''' |
| 4502 | + Restores a deleted server. |
| 4503 | + ''' |
| 4504 | + # Validate that we have enough information to perform the restore |
| 4505 | + if not server_name or not resource_group_name or not location: |
| 4506 | + logger.info('Not all required parameters were provided. ' |
| 4507 | + 'Please specify server name, resource group name, and location.') |
| 4508 | + return None |
| 4509 | + |
| 4510 | + # Check if server already exists |
| 4511 | + _check_server_exists(client, resource_group_name, server_name) |
| 4512 | + |
| 4513 | + # Validate deleted server exists |
| 4514 | + _validate_deleted_server_exists(cmd, location, server_name) |
| 4515 | + |
| 4516 | + # Set required parameters for restore |
| 4517 | + kwargs['location'] = location |
| 4518 | + kwargs['create_mode'] = ServerCreateMode.RESTORE |
| 4519 | + |
| 4520 | + logger.info('Attempting to restore server "%s" in location "%s"', |
| 4521 | + server_name, location) |
| 4522 | + |
| 4523 | + # Create/restore the server |
| 4524 | + return sdk_no_wait(no_wait, client.begin_create_or_update, |
| 4525 | + server_name=server_name, |
| 4526 | + resource_group_name=resource_group_name, |
| 4527 | + parameters=kwargs) |
| 4528 | + |
| 4529 | + |
4424 | 4530 | def server_list( |
4425 | 4531 | client, |
4426 | 4532 | resource_group_name=None, |
@@ -4469,7 +4575,9 @@ def server_update( |
4469 | 4575 | key_id=None, |
4470 | 4576 | federated_client_id=None, |
4471 | 4577 | identity_type=None, |
4472 | | - user_assigned_identity_id=None): |
| 4578 | + user_assigned_identity_id=None, |
| 4579 | + enable_soft_delete=None, |
| 4580 | + soft_delete_retention_days=None): |
4473 | 4581 | ''' |
4474 | 4582 | Updates a server. Custom update function to apply parameters to instance. |
4475 | 4583 | ''' |
@@ -4507,6 +4615,17 @@ def server_update( |
4507 | 4615 | instance.key_id = (key_id or instance.key_id) |
4508 | 4616 | instance.federated_client_id = (federated_client_id or instance.federated_client_id) |
4509 | 4617 |
|
| 4618 | + # Handle soft delete parameters |
| 4619 | + if enable_soft_delete is not None: |
| 4620 | + if soft_delete_retention_days is not None: |
| 4621 | + instance.retention_days = soft_delete_retention_days |
| 4622 | + elif enable_soft_delete: |
| 4623 | + # Set default to 7 days when enabling soft delete without specifying retention |
| 4624 | + instance.retention_days = 7 |
| 4625 | + else: |
| 4626 | + # When disabling soft delete, clear retention days |
| 4627 | + instance.retention_days = 0 |
| 4628 | + |
4510 | 4629 | return instance |
4511 | 4630 |
|
4512 | 4631 |
|
|
0 commit comments