Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
498 changes: 498 additions & 0 deletions doc/sql-instance-cli-cmdlets.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
# --------------------------------------------------------------------------------------------

import unittest
from io import BytesIO
import os
import inspect
import tempfile
import shutil
import logging
import threading
import vcr
from vcr.request import Request

from .config import TestConfig
from .const import ENV_TEST_DIAGNOSE
Expand Down Expand Up @@ -178,6 +180,10 @@ def _process_request_recording(self, request):
if self.disable_recording:
return None

body = request.body
if isinstance(body, BytesIO):
request = Request(request.method, request.uri, body.getvalue(), request.headers)

if self.in_recording:
for processor in self.recording_processors + self.recording_post_processors:
request = processor.process_request(request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,58 @@

import os
import unittest
from io import BytesIO
from types import SimpleNamespace

from azure.cli.testsdk.scenario_tests.base import IntegrationTestBase, LiveTest
from vcr.request import Request

from azure.cli.testsdk.scenario_tests.base import IntegrationTestBase, LiveTest, ReplayableTest
from azure.cli.testsdk.scenario_tests.recording_processors import (
LargeRequestBodyProcessor, SubscriptionRecordingProcessor
)


class TestIntegrationTestBase(unittest.TestCase):
def test_process_request_recording_with_file_body(self):
original_subscription = '11111111-1111-1111-1111-111111111111'
replacement_subscription = '00000000-0000-0000-0000-000000000000'
uri = 'https://management.azure.com/subscriptions/' + original_subscription
body = ('{"id":"/subscriptions/' + original_subscription + '"}').encode()
for recording in (True, False):
for stream in (True, False):
with self.subTest(recording=recording, stream=stream):
request = Request('POST', uri, BytesIO(body) if stream else body,
{'Content-Type': 'application/json'})
processor = SubscriptionRecordingProcessor(replacement_subscription)
test = SimpleNamespace(
disable_recording=False, in_recording=recording,
recording_processors=[processor],
recording_post_processors=[LargeRequestBodyProcessor()],
replay_processors=[processor, LargeRequestBodyProcessor()])
processed = ReplayableTest._process_request_recording(test, request)
self.assertEqual(processed.method, 'POST')
self.assertEqual(processed.uri, uri.replace(original_subscription, replacement_subscription))
self.assertEqual(processed.headers, request.headers)
self.assertEqual(processed.body,
body.replace(original_subscription.encode(), replacement_subscription.encode()))
if stream:
self.assertEqual(request.body.getvalue(), body)
self.assertEqual(request.body.tell(), 0)
else:
self.assertIs(processed, request)

def test_process_request_recording_preserves_binary_file_body(self):
body = b'\x00\xff\x01'
request = Request('POST', 'https://management.azure.com/upload', BytesIO(body),
{'Content-Type': 'application/octet-stream'})
test = SimpleNamespace(
disable_recording=False, in_recording=True,
recording_processors=[SubscriptionRecordingProcessor('unused')],
recording_post_processors=[LargeRequestBodyProcessor()])
processed = ReplayableTest._process_request_recording(test, request)
self.assertEqual(processed.body, body)
self.assertEqual(request.body.getvalue(), body)

def test_integration_test_default_constructor(self):
class MockTest(IntegrationTestBase):
def __init__(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ def protection_container_refresh_operation_results_cf(cli_ctx, *_):
return _backup_client_factory(cli_ctx).protection_container_refresh_operation_results


def protection_container_refresh_operation_statuses_cf(cli_ctx, *_):
return _backup_client_factory(cli_ctx).protection_container_refresh_operation_statuses


def protected_items_cf(cli_ctx, *_):
return _backup_client_factory(cli_ctx).protected_items

Expand Down
27 changes: 26 additions & 1 deletion src/azure-cli/azure/cli/command_modules/backup/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,9 @@
short-summary: Start protecting a previously unprotected workload within an Azure VM as per the specified policy to a Recovery services vault. Provide the workload details as a protectable item.
examples:
- name: Start protecting a previously unprotected workload within an Azure VM as per the specified policy to a Recovery services vault. Provide the workload details as a protectable item.
text: az backup protection enable-for-azurewl --policy-name MyPolicy --resource-group MyResourceGroup --vault-name MyVault --protectable-item-name ItemName --protectable-item-type SQLInstance --server-name Myserver --workload-type MSSQL
text: az backup protection enable-for-azurewl --policy-name MyPolicy --resource-group MyResourceGroup --vault-name MyVault --protectable-item-name ItemName --protectable-item-type SQLDatabase --server-name Myserver --workload-type MSSQL
- name: Protect selected databases under a SQL instance snapshot policy.
text: az backup protection enable-for-azurewl --policy-name SqlInstanceSnapshotPolicy --resource-group MyResourceGroup --vault-name MyVault --protectable-item-name "SQLInstance;mssqlserver" --protectable-item-type SQLInstance --server-name MyServer --workload-type SQLInstance --child-database-names Database1 Database2
"""

helps['backup protection auto-enable-for-azurewl'] = """
Expand Down Expand Up @@ -372,9 +374,21 @@
helps['backup recoveryconfig show'] = """
type: command
short-summary: Construct the recovery configuration of an Azure workload backed up item.
long-summary: >
For SQL snapshot restores, provide --snapshot-rg and --identity-arm-id. Inspect the generated
target_physical_path entries and edit target_path only for volumes that need remapping.
Use --recovery-mode SnapshotAttach to attach snapshot disks without recovering databases,
or SnapshotAttachAndRecover to recover databases. Use --no-recovery-mode to leave databases
in the restoring state. For cross-region restore, generate the configuration normally and specify
--use-secondary-region only when submitting it with az backup restore restore-azurewl.
Configuration generation reads primary-region metadata. Select a recovery point available in the
secondary region and the intended target instance, disk resource group and managed identity for
that region. Restore validates the snapshot and log availability in the secondary region.
examples:
- name: Construct the recovery configuration of an Azure workload backed up item. The configuration object stores all details such as the recovery mode, target destinations for the restore and application specific parameters like target physical paths for SQL.
text: az backup recoveryconfig show --container-name MyContainer --item-name MyItem --resource-group MyResourceGroup --vault-name MyVault --restore-mode OriginalWorkloadRestore
- name: Generate a SQL instance snapshot original-location recovery configuration.
text: az backup recoveryconfig show --container-name MyContainer --item-name "SQLInstance;mssqlserver" --resource-group MyResourceGroup --vault-name MyVault --workload-type SQLInstance --rp-name MySnapshotRp --restore-mode OriginalWorkloadRestore --recovery-mode SnapshotAttachAndRecover --snapshot-rg RestoreResourceGroup --identity-arm-id "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/IdentityResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/MyIdentity"
"""

helps['backup recoverypoint'] = """
Expand All @@ -389,6 +403,8 @@
- name: List all recovery points of a backed up item. (autogenerated)
text: az backup recoverypoint list --container-name MyContainer --backup-management-type AzureIaasVM --item-name MyItem --resource-group MyResourceGroup --vault-name MyVault
crafted: true
- name: Get the latest SQL instance snapshot recovery point.
text: az backup recoverypoint list --container-name MyContainer --item-name "SQLInstance;mssqlserver" --resource-group MyResourceGroup --vault-name MyVault --backup-management-type AzureWorkload --workload-type SQLInstance --restore-point-query-type Snapshot --get-latest-recovery-point-only true
"""

helps['backup recoverypoint move'] = """
Expand All @@ -414,6 +430,8 @@
examples:
- name: List the start and end points of the unbroken log chain(s) of the given backup item. Use it to determine whether the point-in-time, to which the user wants the DB to be restored, is valid or not.
text: az backup recoverypoint show-log-chain --container-name MyContainer --backup-management-type AzureWorkload --item-name MyItem --resource-group MyResourceGroup --vault-name MyVault
- name: Query database log ranges on top of a SQL instance snapshot. Use the instance ProtectedItemDataSourceId as the base datasource.
text: az backup recoverypoint show-log-chain --container-name MyContainer --backup-management-type AzureWorkload --item-name "SQLDataBase;mssqlserver;MyDatabase" --resource-group MyResourceGroup --vault-name MyVault --workload-type MSSQL --recovery-mode SnapshotAttachAndRecover --base-snapshot-recovery-point-id MySnapshotRp --base-recovery-point-datasource-id MyInstanceDatasourceId
"""

helps['backup restore'] = """
Expand Down Expand Up @@ -494,9 +512,16 @@
helps['backup restore restore-azurewl'] = """
type: command
short-summary: Restore backed up Azure Workloads in a Recovery services vault to another registered container or to the same container.
long-summary: >
SQL snapshot cross-region restores support SnapshotAttach and SnapshotAttachAndRecover, including
point-in-time recovery. Generate the recovery configuration normally and specify
--use-secondary-region only when submitting the restore. The vault must support cross-region restore and the
selected recovery point must be available in the secondary region.
examples:
- name: Restore backed up Azure Workloads in a Recovery services vault to another registered container or to the same container.
text: az backup restore restore-azurewl --resource-group MyResourceGroup --vault-name MyVault --recovery-config MyRecoveryConfig
- name: Submit a SQL snapshot restore using a secondary-region recovery configuration.
text: az backup restore restore-azurewl --resource-group MyResourceGroup --vault-name MyVault --recovery-config secondary-recovery-config.json --use-secondary-region
"""

helps['backup vault'] = """
Expand Down
23 changes: 17 additions & 6 deletions src/azure-cli/azure/cli/command_modules/backup/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
# ARGUMENT DEFINITIONS

allowed_container_types = ['AzureIaasVM']
allowed_workload_types = ['VM', 'AzureFileShare', 'SAPHANA', 'SAPASE', 'MSSQL', 'SAPHanaDatabase', 'SQLDataBase', 'SAPAseDatabase', 'SAPHanaDBInstance']
allowed_azure_workload_types = ['MSSQL', 'SAPHANA', 'SAPASE', 'SAPAseDatabase', 'SAPHanaDatabase', 'SQLDataBase', 'SAPHanaDBInstance']
allowed_workload_types = ['VM', 'AzureFileShare', 'SAPHANA', 'SAPASE', 'MSSQL', 'SAPHanaDatabase', 'SQLDataBase', 'SAPAseDatabase', 'SAPHanaDBInstance', 'SQLInstance']
allowed_azure_workload_types = ['MSSQL', 'SAPHANA', 'SAPASE', 'SAPAseDatabase', 'SAPHanaDatabase', 'SQLDataBase', 'SAPHanaDBInstance', 'SQLInstance']
allowed_container_registration_workload_types = allowed_azure_workload_types + ['AzureFileShare']
allowed_backup_management_types = ['AzureIaasVM', 'AzureStorage', 'AzureWorkload']
allowed_extended_backup_management_types = allowed_backup_management_types + ['MAB']
Expand All @@ -40,15 +40,15 @@

backup_management_type_help = """Specify the backup management type. Define how Azure Backup manages the backup of entities within the ARM resource. For eg: AzureWorkloads refers to workloads installed within Azure VMs, AzureStorage refers to entities within Storage account. Required only if friendly name is used as Container name."""
container_name_help = """Name of the backup container. Accepts 'Name' or 'FriendlyName' from the output of az backup container list command. If 'FriendlyName' is passed then BackupManagementType is required."""
workload_type_help = """Specify the type of applications within the Resource which should be discovered and protected by Azure Backup. 'MSSQL' and 'SQLDataBase' can be used interchangeably for SQL in Azure VM, as can 'SAPHANA' and 'SAPHanaDatabase' for SAP HANA in Azure VM."""
workload_type_help = """Specify the type of applications within the Resource which should be discovered and protected by Azure Backup. 'MSSQL' and 'SQLDataBase' can be used interchangeably for SQL databases in Azure VM. Use 'SQLInstance' for SQL instance snapshot protection. 'SAPHANA' and 'SAPHanaDatabase' are interchangeable for SAP HANA in Azure VM."""
protectable_item_type_help = """Specify the type of items within the Resource which should be discovered and protected by Azure Backup. 'HANAInstance' and 'SAPHanaSystem' can be used interchangeably."""
restore_mode_help = """Specify the restore mode."""
resolve_conflict_help = "Instruction if there's a conflict with the restored data."
resource_id_help = """ID of the Azure Resource containing items to be protected by Azure Backup service. Currently, only Azure VM resource IDs are supported."""
policy_help = """JSON encoded policy definition. Use the show command with JSON output to obtain a policy object. Modify the values using a file editor and pass the object."""
target_server_type_help = """Specify the type of the server which should be discovered."""
protectable_item_name_type_help = """Specify the resource name to be protected by Azure Backup service."""
backup_type_help = """'Full, Differential, Log, CopyOnlyFull' for backup Item type 'MSSQL'. 'Full, Differential' for backup item type 'SAPHANA'."""
backup_type_help = """'Full, Differential, Log, CopyOnlyFull' for backup item type 'MSSQL'. 'SnapshotFull' for 'SQLInstance'. 'Full, Differential' for backup item type 'SAPHANA'."""
retain_until_help = """The date until which this backed up copy will be available for retrieval, in UTC (d-m-Y). In case of VM and AzureFileShare a default value of 30 days is taken. For MSSQL workload, retain-until min value is 2 days and max value is 99 years for backup-type 'CopyOnlyFull'. For MSSQL and SAPHANA, retain-until min value is 45 days and max value is 99 years for backup-type 'Full'. For any other backup-type, retain-until value is overriden by Policy. If not specified, a default value of 30 days will be taken for backup-type 'CopyOnlyFull', and a value of 45 days for backup-type 'Full' and no default value for other backup types."""
diskslist_help = """List of disks to be excluded or included."""
disk_list_setting_help = """option to decide whether to include or exclude the disk or reset any previous settings to default behavior"""
Expand Down Expand Up @@ -208,6 +208,7 @@ def load_arguments(self, _):
c.argument('vault_name', vault_name_type, id_part=None)
c.argument('backup_management_type', backup_management_type)
c.argument('resource_id', resource_id_type)
c.argument('container_subscription_id', options_list=['--container-subscription-id', '--container-sub-id'], help='Subscription containing the VM to discover. The vault remains in the subscription selected by --subscription.')
c.argument('workload_type', container_registration_workload_type)
c.argument('storage_account', options_list=['--storage-account'],
help='Name of the storage account to register for Azure Files backup.')
Expand Down Expand Up @@ -310,6 +311,8 @@ def load_arguments(self, _):
c.argument('target_tier', target_tier_type)
c.argument('tier', tier_type)
c.argument('recommended_for_archive', action="store_true", help='Use this flag to retrieve recommended archivable recoverypoints.')
c.argument('restore_point_query_type', options_list=['--restore-point-query-type', '--rp-query-type'], arg_type=get_enum_type(['Full', 'Log', 'Differential', 'FullAndDifferential', 'All', 'Incremental', 'SnapshotFull', 'SnapshotCopyOnlyFull', 'Snapshot', 'FullDifferentialAndSnapshot']), help='Filter AzureWorkload recovery points by backup type.')
c.argument('get_latest_recovery_point_only', options_list=['--get-latest-recovery-point-only', '--latest-rp-only'], arg_type=get_three_state_flag(), help='Return only the latest matching AzureWorkload recovery point.')

with self.argument_context('backup recoverypoint move') as c:
c.argument('vault_name', vault_name_type, id_part=None)
Expand All @@ -328,6 +331,9 @@ def load_arguments(self, _):
c.argument('container_name', container_name_type)
c.argument('workload_type', workload_type)
c.argument('use_secondary_region', action='store_true', help='Use this flag to list recoverypoints in secondary region.')
c.argument('recovery_mode', arg_type=get_enum_type(['SnapshotAttachAndRecover']), help='Query database log ranges on top of an instance snapshot.')
c.argument('base_snapshot_recovery_point_id', options_list=['--base-snapshot-recovery-point-id', '--base-snapshot-rp-id'], help='Name of the base instance snapshot recovery point.')
c.argument('base_recovery_point_datasource_id', options_list=['--base-recovery-point-datasource-id', '--base-datasource-id'], help='ProtectedItemDataSourceId of the base SQL instance, not the child database.')

with self.argument_context('backup recoverypoint show') as c:
c.argument('name', rp_name_type, options_list=['--name', '-n'], help='Name of the recovery point. You can use the backup recovery point list command to get the name of a backed up item.', id_part='child_name_4')
Expand Down Expand Up @@ -409,6 +415,9 @@ def load_arguments(self, _):
c.argument('server_name', options_list=['--server-name'], help='Parent Server name of the item.')
c.argument('workload_type', workload_type)

with self.argument_context('backup protection enable-for-azurewl') as c:
c.argument('child_database_names', nargs='+', help='Databases to protect under a SQL instance snapshot policy.')

# Protectable-item
with self.argument_context('backup protectable-item') as c:
c.argument('vault_name', vault_name_type)
Expand Down Expand Up @@ -527,8 +536,10 @@ def load_arguments(self, _):
c.argument('target_vault_name', options_list=['--target-vault-name'], help="""Specify the vault name of target item for Cross Region Restore. Default value will be same as --vault-name if not specified.""")
c.argument('target_subscription_id', help="""Specify the subscription of the target item for Cross Subscription Restore. Defaulted to source subscription if not specified.""")
c.argument('attach_and_mount', arg_type=get_three_state_flag(), help='Specify attach and mount value for HANA Snapshot restores.')
c.argument('identity_arm_id', help='Set Identity ARM ID for HANA Snapshot restores.')
c.argument('snapshot_instance_resource_group', options_list=['--snapshot-rg'], help="""Specify the resource group for HANA Snapshot Instance restores. If not provided, the default value will be fetched from the target container details.""")
c.argument('identity_arm_id', help='ARM ID of the user-assigned managed identity for SQL or HANA snapshot restores.')
c.argument('snapshot_instance_resource_group', options_list=['--snapshot-rg'], help="""Resource group for restored snapshot disks. Required for SQL snapshot restores; defaults to the target container resource group for HANA.""")
c.argument('recovery_mode', arg_type=get_enum_type(['SnapshotAttachAndRecover', 'SnapshotAttach']), help='SQL snapshot recovery mode. SnapshotAttach attaches disks without recovering databases.')
c.argument('no_recovery_mode', action='store_true', help='Leave restored SQL databases in the restoring state.')

# Job
with self.argument_context('backup job') as c:
Expand Down
Loading
Loading