Skip to content

Commit a279b46

Browse files
kumarutkarsh3b2166Zubair Abid
andauthored
[Backup] az backup container/item/policy/protection: Add support for ASE backup operations (#31413)
Co-authored-by: Zubair Abid <zubairabid@microsoft.com>
1 parent 106221b commit a279b46

65 files changed

Lines changed: 84777 additions & 147720 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/azure-cli/azure/cli/command_modules/backup/_params.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
# ARGUMENT DEFINITIONS
2222

2323
allowed_container_types = ['AzureIaasVM']
24-
allowed_workload_types = ['VM', 'AzureFileShare', 'SAPHANA', 'MSSQL', 'SAPHanaDatabase', 'SQLDataBase']
25-
allowed_azure_workload_types = ['MSSQL', 'SAPHANA', 'SAPASE', 'SAPHanaDatabase', 'SQLDataBase']
24+
allowed_workload_types = ['VM', 'AzureFileShare', 'SAPHANA', 'SAPASE', 'MSSQL', 'SAPHanaDatabase', 'SQLDataBase', 'SAPAseDatabase']
25+
allowed_azure_workload_types = ['MSSQL', 'SAPHANA', 'SAPASE', 'SAPAseDatabase', 'SAPHanaDatabase', 'SQLDataBase']
2626
allowed_backup_management_types = ['AzureIaasVM', 'AzureStorage', 'AzureWorkload']
2727
allowed_extended_backup_management_types = allowed_backup_management_types + ['MAB']
28-
allowed_protectable_item_type = ['SQLAG', 'SQLInstance', 'SQLDatabase', 'HANAInstance', 'SAPHanaDatabase', 'SAPHanaSystem']
28+
allowed_protectable_item_type = ['SQLAG', 'SQLInstance', 'SQLDatabase', 'HANAInstance', 'SAPAseDatabase', 'SAPHanaDatabase', 'SAPHanaSystem']
2929
allowed_target_tier_type_chk_archivable = ['VaultArchive']
3030
allowed_tier_type = ['VaultStandard', 'Snapshot', 'VaultArchive', 'VaultStandardRehydrated', 'SnapshotAndVaultStandard', 'SnapshotAndVaultArchive']
3131
allowed_rehyd_priority_type = ['Standard', 'High']

src/azure-cli/azure/cli/command_modules/backup/_validators.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ def validate_wl_restore(item, item_type, restore_mode, recovery_mode):
5353
# operation. Correct value should be - {}.
5454
# """.format(item.properties.workload_type))
5555

56-
if item_type is None or item_type.lower() not in ['sql', 'saphana']:
56+
if item_type is None or item_type.lower() not in ['sql', 'saphana', 'sapase']:
5757
raise InvalidArgumentValueError("""
5858
The item_type specified in recovery config file is incorrect. Please correct it and retry the
59-
operation. Allowed values are: 'SQL', 'SAPHana'.
59+
operation. Allowed values are: 'SQL', 'SAPHana', 'SAPAse'.
6060
""")
6161

6262
if item_type.lower() not in item.properties.workload_type.lower():

src/azure-cli/azure/cli/command_modules/backup/custom_common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
'SAPHANA': 'SAPHanaDatabase',
2424
'SQLDataBase': 'SQLDataBase',
2525
'SAPHanaDatabase': 'SAPHanaDatabase',
26+
'SAPAseDatabase': 'SAPAseDatabase',
2627
'VM': 'VM',
2728
'AzureFileShare': 'AzureFileShare'}
2829

2930
workload_bmt_map = {'SQLDataBase': 'AzureWorkload',
3031
'SAPHanaDatabase': 'AzureWorkload',
32+
'SAPAseDatabase': 'AzureWorkload',
3133
'VM': 'AzureIaasVM',
3234
'AzureFileShare': 'AzureStorage'}
3335

src/azure-cli/azure/cli/command_modules/backup/custom_help.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ def is_hana(resource_type):
7474
return resource_type.lower() == 'saphanadatabase'
7575

7676

77+
def is_sapase(resource_type):
78+
return resource_type.lower() == 'sapasedatabase'
79+
80+
7781
def is_wl_container(name):
7882
return 'vmappcontainer' in name.lower()
7983

src/azure-cli/azure/cli/command_modules/backup/custom_wl.py

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
AzureWorkloadBackupRequest, ProtectedItemResource, AzureRecoveryServiceVaultProtectionIntent, TargetRestoreInfo, \
1818
RestoreRequestResource, BackupRequestResource, ProtectionIntentResource, SQLDataDirectoryMapping, \
1919
ProtectionContainerResource, AzureWorkloadSAPHanaRestoreRequest, AzureWorkloadSQLRestoreRequest, \
20-
AzureWorkloadSAPHanaPointInTimeRestoreRequest, AzureWorkloadSQLPointInTimeRestoreRequest, \
21-
AzureVmWorkloadSAPHanaDatabaseProtectedItem, AzureVmWorkloadSQLDatabaseProtectedItem, MoveRPAcrossTiersRequest, \
20+
AzureWorkloadSAPAseRestoreRequest, AzureWorkloadSAPHanaPointInTimeRestoreRequest, \
21+
AzureWorkloadSQLPointInTimeRestoreRequest, AzureWorkloadSAPAsePointInTimeRestoreRequest, \
22+
AzureVmWorkloadSAPHanaDatabaseProtectedItem, AzureVmWorkloadSQLDatabaseProtectedItem, \
2223
RecoveryPointRehydrationInfo, AzureWorkloadSAPHanaRestoreWithRehydrateRequest, \
23-
AzureWorkloadSQLRestoreWithRehydrateRequest, ProtectionState
24+
AzureWorkloadSQLRestoreWithRehydrateRequest, ProtectionState, AzureVmWorkloadSAPAseDatabaseProtectedItem, \
25+
MoveRPAcrossTiersRequest \
2426

2527
from azure.mgmt.recoveryservicesbackup.passivestamp.models import CrossRegionRestoreRequest
2628

@@ -49,7 +51,8 @@
4951
'SAPHANA': 'SAPHanaDatabase',
5052
'SQLDataBase': 'SQLDataBase',
5153
'SAPHanaDatabase': 'SAPHanaDatabase',
52-
'SAPASE': 'SAPAseDatabase'}
54+
'SAPASE': 'SAPAseDatabase',
55+
'SAPAseDatabase': 'SAPAseDatabase'}
5356

5457
# Mapping of module name
5558
module_map = {'sqldatabase': 'sql_database',
@@ -67,7 +70,9 @@
6770
'HANAInstance': 'SAPHanaSystem',
6871
'SAPHanaSystem': 'SAPHanaSystem',
6972
'SQLInstance': 'SQLInstance',
70-
'SQLAG': 'SQLAvailabilityGroupContainer'}
73+
'SQLAG': 'SQLAvailabilityGroupContainer',
74+
'SAPASE': 'SAPAseDatabase',
75+
'SAPAseDatabase': 'SAPAseDatabase'}
7176

7277

7378
def show_wl_policy(client, resource_group_name, vault_name, name):
@@ -203,8 +208,10 @@ def update_policy_for_item(cmd, client, resource_group_name, vault_name, item, p
203208
item_uri = cust_help.get_protected_item_uri_from_id(item.id)
204209

205210
backup_item_type = item_uri.split(';')[0]
206-
if not cust_help.is_sql(backup_item_type) and not cust_help.is_hana(backup_item_type):
207-
raise InvalidArgumentValueError("Item must be either of type SQLDataBase or SAPHanaDatabase.")
211+
if (not cust_help.is_sql(backup_item_type) and not
212+
cust_help.is_hana(backup_item_type) and not
213+
cust_help.is_sapase(backup_item_type)):
214+
raise InvalidArgumentValueError("Item must be of type SQLDataBase, SAPHanaDatabase, or SAPAseDatabase")
208215

209216
item_properties = _get_protected_item_instance(backup_item_type)
210217
item_properties.policy_id = policy.id
@@ -434,10 +441,12 @@ def enable_protection_for_azure_wl(cmd, client, resource_group_name, vault_name,
434441
# Get protectable item.
435442
protectable_item_object = protectable_item
436443
protectable_item_type = protectable_item_object.properties.protectable_item_type
437-
if protectable_item_type.lower() not in ["sqldatabase", "sqlinstance", "saphanadatabase", "saphanasystem"]:
444+
if protectable_item_type.lower() not in ["sqldatabase", "sqlinstance", "saphanadatabase", "saphanasystem",
445+
"sapasedatabase"]:
438446
raise CLIError(
439447
"""
440-
Protectable Item must be either of type SQLDataBase, HANADatabase, HANAInstance or SQLInstance.
448+
Protectable Item must be either of type SQLDataBase, HANADatabase, HANAInstance, SAPAseDatabase or
449+
SQLInstance.
441450
""")
442451

443452
item_name = protectable_item_object.name
@@ -794,7 +803,7 @@ def show_recovery_config(cmd, client, resource_group_name, vault_name, restore_m
794803
item_type = item.properties.workload_type
795804
item_name = item.name
796805

797-
if not cust_help.is_sql(item_type) and not cust_help.is_hana(item_type):
806+
if not cust_help.is_sql(item_type) and not cust_help.is_hana(item_type) and not cust_help.is_sapase(item_type):
798807
raise CLIError(
799808
"""
800809
Item must be either of type SQLDataBase or SAPHanaDatabase.
@@ -860,7 +869,7 @@ def show_recovery_config(cmd, client, resource_group_name, vault_name, restore_m
860869
'item_uri': item_name,
861870
'recovery_point_id': recovery_point.name,
862871
'log_point_in_time': log_point_in_time,
863-
'item_type': 'SQL' if 'sql' in item_type.lower() else 'SAPHana',
872+
'item_type': 'SQL' if 'sql' in item_type.lower() else 'SAPASE' if 'sapase' in item_type.lower() else 'SAPHana',
864873
'workload_type': item_type,
865874
'source_resource_id': item.properties.source_resource_id,
866875
'database_name': db_name,
@@ -931,16 +940,22 @@ def _get_log_time_range(cmd, resource_group_name, vault_name, item, use_secondar
931940

932941

933942
def _get_restore_request_instance(item_type, log_point_in_time, rehydration_priority):
934-
if rehydration_priority is None:
935-
if item_type.lower() == "saphana":
936-
if log_point_in_time is not None:
937-
return AzureWorkloadSAPHanaPointInTimeRestoreRequest()
938-
return AzureWorkloadSAPHanaRestoreRequest()
943+
workload_restore_request_map = {
944+
"saphana": AzureWorkloadSAPHanaRestoreRequest,
945+
"sql": AzureWorkloadSQLRestoreRequest,
946+
"sapase": AzureWorkloadSAPAseRestoreRequest
947+
}
948+
949+
workload_pit_restore_request_map = {
950+
"saphana": AzureWorkloadSAPHanaPointInTimeRestoreRequest,
951+
"sql": AzureWorkloadSQLPointInTimeRestoreRequest,
952+
"sapase": AzureWorkloadSAPAsePointInTimeRestoreRequest
953+
}
939954

940-
if item_type.lower() == "sql":
941-
if log_point_in_time is not None:
942-
return AzureWorkloadSQLPointInTimeRestoreRequest()
943-
return AzureWorkloadSQLRestoreRequest()
955+
if rehydration_priority is None:
956+
if log_point_in_time is not None:
957+
return workload_pit_restore_request_map[item_type.lower()]()
958+
return workload_restore_request_map[item_type.lower()]()
944959

945960
if item_type.lower() == "saphana":
946961
if log_point_in_time is not None:
@@ -956,6 +971,8 @@ def _get_restore_request_instance(item_type, log_point_in_time, rehydration_prio
956971
def _get_protected_item_instance(item_type):
957972
if item_type.lower() == "saphanadatabase":
958973
return AzureVmWorkloadSAPHanaDatabaseProtectedItem()
974+
if item_type.lower() == "sapasedatabase":
975+
return AzureVmWorkloadSAPAseDatabaseProtectedItem()
959976
return AzureVmWorkloadSQLDatabaseProtectedItem()
960977

961978

src/azure-cli/azure/cli/command_modules/backup/tests/latest/preparers.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,38 @@
1616
logger = get_logger(__name__)
1717

1818

19+
# Temporary Resource Group Preparer for testing while we update the RecoveryServices SDK to deal with new Soft Delete rules
20+
class RGPreparer(AbstractPreparer, SingleValueReplacer):
21+
def __init__(self, name_prefix='clitest.rg',
22+
parameter_name='resource_group',
23+
parameter_name_for_location='resource_group_location', location='westus',
24+
dev_setting_name='AZURE_CLI_TEST_DEV_RESOURCE_GROUP_NAME',
25+
dev_setting_location='AZURE_CLI_TEST_DEV_RESOURCE_GROUP_LOCATION',
26+
random_name_length=75, key='rg', subscription=None, additional_tags=None):
27+
if ' ' in name_prefix:
28+
raise CliTestError('Error: Space character in resource group name prefix \'%s\'' % name_prefix)
29+
super().__init__(name_prefix, random_name_length)
30+
from azure.cli.core.mock import DummyCli
31+
self.cli_ctx = DummyCli()
32+
self.location = location
33+
self.subscription = subscription
34+
self.parameter_name = parameter_name
35+
self.parameter_name_for_location = parameter_name_for_location
36+
self.key = key
37+
self.additional_tags = additional_tags
38+
39+
self.dev_setting_name = os.environ.get(dev_setting_name, None)
40+
self.dev_setting_location = os.environ.get(dev_setting_location, location)
41+
42+
def create_resource(self, name, **kwargs):
43+
cmd = 'az group create --location {} --name {}'.format(self.location, name)
44+
execute(self.cli_ctx, cmd)
45+
return {self.parameter_name: name, self.parameter_name_for_location: self.location}
46+
47+
def remove_resource(self, name, **kwargs):
48+
pass
49+
50+
1951
class VaultPreparer(AbstractPreparer, SingleValueReplacer): # pylint: disable=too-many-instance-attributes
2052
def __init__(self, name_prefix='clitest-vault', parameter_name='vault_name',
2153
resource_group_location_parameter_name='resource_group_location',
@@ -87,14 +119,13 @@ def _cleanup(self, vault_name, resource_group):
87119
try:
88120
execute(self.cli_ctx, 'az backup vault delete -n {} -g {} --yes'.format(vault_name, resource_group))
89121
except HttpResponseError as ex:
90-
if "Operation returned an invalid status 'Bad Request'" not in str(ex):
91-
raise ex
122+
logger.warning('Unable to delete the vault. Please delete it manually.')
92123

93124

94125
class VMPreparer(AbstractPreparer, SingleValueReplacer):
95126
def __init__(self, name_prefix='clitest-vm', parameter_name='vm_name',
96127
resource_group_location_parameter_name='resource_group_location',
97-
resource_group_parameter_name='resource_group', dev_setting_name='AZURE_CLI_TEST_DEV_BACKUP_VM_NAME', image = "Win2012R2Datacenter"):
128+
resource_group_parameter_name='resource_group', dev_setting_name='AZURE_CLI_TEST_DEV_BACKUP_VM_NAME', image = "Win2022Datacenter"):
98129
super().__init__(name_prefix, 15)
99130
from azure.cli.core.mock import DummyCli
100131
self.cli_ctx = DummyCli()
@@ -111,18 +142,23 @@ def create_resource(self, name, **kwargs):
111142
self.resource_group = self._get_resource_group(**kwargs)
112143
self.location = self._get_resource_group_location(**kwargs)
113144
param_format = '-n {} -g {} --image {} --admin-username {} --admin-password {} '
114-
param_format += '--tags {} --nsg-rule None --security-type {}'
145+
param_format += '--tags {} --nsg-rule None'
115146
# param_format += '--tags {} --size {} --nsg-rule None'
116147
param_tags = 'MabUsed=Yes Owner=sisi Purpose=CLITest DeleteBy=12-2099 AutoShutdown=No'
117148
param_string = param_format.format(name, self.resource_group, self.image, name,
118-
'%j^VYw9Q3Z@Cu$*h', param_tags, "Standard") #, 'Standard_D2a_v4')
149+
'%j^VYw9Q3Z@Cu$*h', param_tags) #, 'Standard_D2a_v4')
119150
cmd = 'az vm create {}'.format(param_string)
120151
execute(self.cli_ctx, cmd)
121152
return {self.parameter_name: name}
122153
return {self.parameter_name: self.dev_setting_value}
123154

124155
def remove_resource(self, name, **kwargs):
125156
# Resource group deletion will take care of this.
157+
cmd = 'az vm delete -g {} -n {} --yes'.format(self.resource_group, name)
158+
try:
159+
execute(self.cli_ctx, cmd)
160+
except:
161+
logger.warning("Unable to delete the Virtual Machine. Please delete it manually.")
126162
pass
127163

128164
def _get_resource_group(self, **kwargs):
@@ -644,7 +680,7 @@ def _delete_lock(self, lock):
644680
command_string = 'az lock delete --ids {}'.format(lock_id)
645681
execute(self.cli_ctx, command_string)
646682
except Exception:
647-
raise CliTestError('Unable to delete the lock with ID {}, please delete it manually'.format(lock_id))
683+
logger.warning('Unable to delete the lock with ID {}, please delete it manually'.format(lock_id))
648684

649685
def _cleanup(self, resource_group, storage_account, vault, afs):
650686
# Need to remove any resource locks on the Storage Account, and also manually delete the item

0 commit comments

Comments
 (0)