diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 45030b887fe..25a5c07e1e4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -324,4 +324,6 @@ /src/azext_durabletask/ @RyanLettieri -/src/acat @qinqingxu @Sherylueen @yongxin-ms @wh-alice \ No newline at end of file +/src/acat @qinqingxu @Sherylueen @yongxin-ms @wh-alice + +/src/zones/ @nielsams diff --git a/src/service_name.json b/src/service_name.json index 4904fb10d97..3ec0c3faaf8 100644 --- a/src/service_name.json +++ b/src/service_name.json @@ -919,6 +919,11 @@ "AzureServiceName": "Microsoft Connected Cache", "URL": "" }, + { + "Command": "az zones", + "AzureServiceName": "Availability Zones", + "URL": "https://learn.microsoft.com/azure/availability-zones/az-overview" + }, { "Command": "az playwright-testing", "AzureServiceName": "Playwright Testing", diff --git a/src/zones/HISTORY.rst b/src/zones/HISTORY.rst new file mode 100644 index 00000000000..d8ade94ae25 --- /dev/null +++ b/src/zones/HISTORY.rst @@ -0,0 +1,8 @@ +.. :changelog: + +Release History +=============== + +1.0.0b1 +++++++ +* Initial preview release. \ No newline at end of file diff --git a/src/zones/README.md b/src/zones/README.md new file mode 100644 index 00000000000..a8abb2dcbe1 --- /dev/null +++ b/src/zones/README.md @@ -0,0 +1,75 @@ +# Microsoft Azure CLI 'zones' Extension + +This package is for the 'zones' extension. +i.e. 'az zones' + +This CLI Extension helps validate the zone redundancy status of resources within a specific scope. +For each resource, one of the following statuses will be returned: + Unknown # Unable to verify status. You'll need to check the resource manually. + Yes # Resource is configured for zone redundancy + Always # Resource is always zone redundant, no configuration needed + No # Resource is not configured for zone redundancy, but could be in another configuration + Never # Resource cannot be configured for zone redundancy + Dependent # Resource is zone redundant if parent or related resource is zone redundant + NoZonesInRegion # The region the resource is deployed in does not have Availability Zones + +> [!NOTE] +> This extension is in active development. While an effort has been made to include the most common resource types and their zone redundancy configuration, there are still plenty of resource types missing. More will be added in future releases. In the meantime, if you need specific resources added or have found errors, please raise a Github issue. + +## When should you use this? + +In order to build a fully zone redundant application, you need to satisfy three criteria: + +1) Enable zone redundancy on all PaaS resources in the application +2) Ensure zonal resources are spread across all zones. These are the resources that take a 'zones' attribute in their definition. +3) Validate that your application code is able to handle the loss of a zone, e.g. that connections are retried properly when a dependency is unreachable. + +The _zones_ CLI extension can help with the first two steps. By running this against a specific resource group that contains your production resources, you can be sure that you have not overlooked any resources in your quest for zone redundancy. If the results show 'No' on one of your resources, that means that you need to change the configuration to enable ZR. If it shows 'Never', that probably means you need to deploy multiple of those resources to the different zones manually. + +The third step can be validated using Chaos Engineering practices. On Azure, look into Chaos Studio to get started with that. + +Suggested use for this extension: +- Manually run this against the production subscription or resource group(s) to validate that all resources have zone redundanct enabled +- Run this as part of your CI/CD pipelines, validating zone redundancy of the resources after deployment in the (pre-)production environment. Consider failing the pipeline if any of the resource results contains _No_ as the result. Note that _no_ only occurs in cases where zone redundancy was not enabled, but could be if the resource was configured differently. + +## USAGE + +Validate all resources in current scope to which you have read access: + +```bash +az zones validate +``` + +Get the results in human-readable table format: + +```bash +az zones validate --output table +``` + +Validate all resources in specific resource groups to which you have read access: + +```bash +az zones validate --resource-groups ,,... +``` + +Omit 'dependent' resources from the output. These are resources that by themselves cannot be zone redundant, but take on the status of their parent or related resource. This can be useful for improving readability of the results: + +```bash +az zones validate --omit-dependent-resources +``` + +Validate all resources with specific tags. Resources that have ALL specified tags will be returned + +```bash +az zones validate --tags env=prod,criticality=high +``` + +## Important Notes + +- The extension still has missing resource types. These are shown as _Unknown_ in the results. It is essential that you validate zone redundancy of these resources yourself, since your whole application is only zone redundant is all resources are zone redundant. + +- The _zones_ CLI extension can only help with resources you can view, i.e. for which you have read access. You must ensure that all relevant resources are indeed listed in the results. + +- While this extension is a useful tool in validating zone redundancy on resources, you are still responsible for reviewing the [Reliability Guides](https://learn.microsoft.com/azure/reliability/overview-reliability-guidance) for all the services you use in your applications, as these may contain important information regarding operation in high availability scenarios. Ultimately, the product reliability guides are the authoritative source for zone redundancy guidance. + +- Zonal services are considered to be Zone Redundant if they are deployed to at least 2 zones. \ No newline at end of file diff --git a/src/zones/azext_zones/__init__.py b/src/zones/azext_zones/__init__.py new file mode 100644 index 00000000000..7fc7b1dbb06 --- /dev/null +++ b/src/zones/azext_zones/__init__.py @@ -0,0 +1,41 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + + +import importlib +from pathlib import Path +from azure.cli.core import AzCommandsLoader +from azext_zones._help import helps # pylint: disable=unused-import + +# Import all the resource type validator modules dynamically: +validators_dir = Path(__file__).parent / "resource_type_validators" +for file in validators_dir.glob("*.py"): + if file.name != "__init__.py": + module_name = f".resource_type_validators.{file.stem}" + importlib.import_module(module_name, package=__package__) + + +class ZonesCommandsLoader(AzCommandsLoader): + + def __init__(self, cli_ctx=None): + from azure.cli.core.commands import CliCommandType + from azext_zones._client_factory import cf_zones + zones_custom = CliCommandType( + operations_tmpl='azext_zones.custom#{}', + client_factory=cf_zones) + super(ZonesCommandsLoader, self).__init__(cli_ctx=cli_ctx, + custom_command_type=zones_custom) + + def load_command_table(self, args): + from azext_zones.commands import load_command_table + load_command_table(self, args) + return self.command_table + + def load_arguments(self, command): + from azext_zones._params import load_arguments + load_arguments(self, command) + + +COMMAND_LOADER_CLS = ZonesCommandsLoader diff --git a/src/zones/azext_zones/_argHelper.py b/src/zones/azext_zones/_argHelper.py new file mode 100644 index 00000000000..c2b79298927 --- /dev/null +++ b/src/zones/azext_zones/_argHelper.py @@ -0,0 +1,146 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import json +from collections import OrderedDict +from knack.util import todict +from knack.log import get_logger + +from .vendored_sdks.resourcegraph.models import ResultTruncated +from .vendored_sdks.resourcegraph.models import QueryRequest, QueryRequestOptions, QueryResponse, ResultFormat, Error +from azure.cli.core._profile import Profile +from azure.core.exceptions import HttpResponseError +from azure.cli.core.azclierror import BadRequestError, AzureInternalError + + +__SUBSCRIPTION_LIMIT = 1000 +__MANAGEMENT_GROUP_LIMIT = 10 +__logger = get_logger(__name__) + + +def build_arg_query(resource_groups, tags): + # type: (list[str], list[str]) -> str + + query = "Resources" + if resource_groups is not None and len(resource_groups) > 0: + query += " | where resourceGroup in ({0})".format(','.join(f"'{item}'" for item in resource_groups.split(','))) + + if tags is not None: + tagquery = [] + for tag in tags.split(','): + tag = tag.strip() + if not tag: # Skip empty tags + continue + + if '=' in tag: + # Tag with a value (TagA=ValueA) + tag_name, tag_value = tag.split('=', 1) + # Escape single quotes in the value + tag_value = tag_value.replace("'", "''") + tagquery.append(f"tags['{tag_name}'] == '{tag_value}'") + else: + # Tag without a value. We don't support those. + pass + + if tagquery: # Only proceed if tagquery has items + query += " | where " + " and ".join(tagquery) + + return query + + +def execute_arg_query( + client, graph_query, first, skip, subscriptions, management_groups, allow_partial_scopes, skip_token): + + mgs_list = management_groups + if mgs_list is not None and len(mgs_list) > __MANAGEMENT_GROUP_LIMIT: + mgs_list = mgs_list[:__MANAGEMENT_GROUP_LIMIT] + warning_message = "The query included more management groups than allowed. "\ + "Only the first {0} management groups were included for the results. "\ + "To use more than {0} management groups, "\ + "see the docs for examples: "\ + "https://aka.ms/arg-error-toomanysubs".format(__MANAGEMENT_GROUP_LIMIT) + __logger.warning(warning_message) + + subs_list = None + if mgs_list is None: + subs_list = subscriptions or _get_cached_subscriptions() + if subs_list is not None and len(subs_list) > __SUBSCRIPTION_LIMIT: + subs_list = subs_list[:__SUBSCRIPTION_LIMIT] + warning_message = "The query included more subscriptions than allowed. "\ + "Only the first {0} subscriptions were included for the results. "\ + "To use more than {0} subscriptions, "\ + "see the docs for examples: "\ + "https://aka.ms/arg-error-toomanysubs".format(__SUBSCRIPTION_LIMIT) + __logger.warning(warning_message) + + response = None + try: + result_truncated = False + + request_options = QueryRequestOptions( + top=first, + skip=skip, + skip_token=skip_token, + result_format=ResultFormat.object_array, + allow_partial_scopes=allow_partial_scopes + ) + + request = QueryRequest( + query=graph_query, + subscriptions=subs_list, + management_groups=mgs_list, + options=request_options) + response = client.resources(request) # type: QueryResponse + if response.result_truncated == ResultTruncated.true: + result_truncated = True + + if result_truncated and first is not None and len(response.data) < first: + __logger.warning("Unable to paginate the results of the query. " + "Some resources may be missing from the results. " + "To rewrite the query and enable paging, " + "see the docs for an example: https://aka.ms/arg-results-truncated") + + except HttpResponseError as ex: + if ex.model.error.code == 'BadRequest': + raise BadRequestError(json.dumps(_to_dict(ex.model.error), indent=4)) from ex + + raise AzureInternalError(json.dumps(_to_dict(ex.model.error), indent=4)) from ex + + result_dict = dict() + result_dict['data'] = response.data + result_dict['count'] = response.count + result_dict['total_records'] = response.total_records + result_dict['skip_token'] = response.skip_token + + return result_dict + + +def _get_cached_subscriptions(): + # type: () -> list[str] + + cached_subs = Profile().load_cached_subscriptions() + return [sub['id'] for sub in cached_subs] + + +def _to_dict(obj): + if isinstance(obj, Error): + return _to_dict(todict(obj)) + + if isinstance(obj, dict): + result = OrderedDict() + + # Complex objects should be displayed last + sorted_keys = sorted(obj.keys(), key=lambda k: (isinstance(obj[k], dict), isinstance(obj[k], list), k)) + for key in sorted_keys: + if obj[key] is None or obj[key] == [] or obj[key] == {}: + continue + + result[key] = _to_dict(obj[key]) + return result + + if isinstance(obj, list): + return [_to_dict(v) for v in obj] + + return obj diff --git a/src/zones/azext_zones/_client_factory.py b/src/zones/azext_zones/_client_factory.py new file mode 100644 index 00000000000..93e05cf4938 --- /dev/null +++ b/src/zones/azext_zones/_client_factory.py @@ -0,0 +1,9 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +def cf_zones(cli_ctx, _): + from azure.cli.core.commands.client_factory import get_mgmt_service_client + from .vendored_sdks.resourcegraph import ResourceGraphClient + return get_mgmt_service_client(cli_ctx, ResourceGraphClient) diff --git a/src/zones/azext_zones/_clients.py b/src/zones/azext_zones/_clients.py new file mode 100644 index 00000000000..c084aaf820c --- /dev/null +++ b/src/zones/azext_zones/_clients.py @@ -0,0 +1,24 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from azure.cli.core.util import send_raw_request +from azure.cli.core.commands.client_factory import get_subscription_id + + +# pylint: disable=too-few-public-methods +class MgmtApiClient(): + + def query(self, cmd, method, resource, api_version, requestBody): + management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager + sub_id = get_subscription_id(cmd.cli_ctx) + url_fmt = ("{}/subscriptions/{}/{}?api-version={}") + request_url = url_fmt.format( + management_hostname.strip('/'), + sub_id, + resource, + api_version) + + r = send_raw_request(cmd.cli_ctx, method, request_url, body=requestBody) + return r.json() diff --git a/src/zones/azext_zones/_help.py b/src/zones/azext_zones/_help.py new file mode 100644 index 00000000000..82f81a73223 --- /dev/null +++ b/src/zones/azext_zones/_help.py @@ -0,0 +1,28 @@ +# coding=utf-8 +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from knack.help_files import helps # pylint: disable=unused-import + + +helps['zones'] = """ + type: group + short-summary: Commands to validate Availability Zone Configuration. Use one of the options below. +""" + +helps['zones validate'] = """ + type: command + short-summary: Validates zone redundancy status of all resources in the current subscription context for which you have read access. + examples: + - name: Validate zone redundancy status of all resources in the specified resource group + text: |- + az zones validate --resource-groups myProductionRG --omit-dependent + - name: Validate zone redundancy status of all resources in the specified resource group, but omit the dependent/child resources + text: |- + az zones validate --resource-groups myProductionRG --omit-dependent + - name: Validate zone redundancy status of all resources that have ALL the specified tags + text: |- + az zones validate --tags env=prod,criticality=high +""" diff --git a/src/zones/azext_zones/_locationDataHelper.py b/src/zones/azext_zones/_locationDataHelper.py new file mode 100644 index 00000000000..040f283204c --- /dev/null +++ b/src/zones/azext_zones/_locationDataHelper.py @@ -0,0 +1,46 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from ._clients import MgmtApiClient +from knack.log import get_logger + + +class LocationDataHelper: + + _location_data = None + _logger = None + + def __init__(self, cmd): + self.cmd = cmd + self._logger = get_logger(__name__) + + def fetch_location_data(self): + if not LocationDataHelper._location_data: + # query(cls, cmd, method, resource, api-version, requestBody): + LocationDataHelper._location_data = MgmtApiClient.query(self, + self.cmd, + "GET", + "locations", + "2022-12-01", + None + ) + + self._logger.debug("Loaded location data successfully.") + + def region_has_zones(self, region): + if LocationDataHelper._location_data is None: + return None + + # While 'global' is not a valid region, we want to return true for global resources + if region == 'global': + return True + + if LocationDataHelper._location_data: + location_data = LocationDataHelper._location_data.get('value', []) + for location in location_data: + if location['name'].lower() == region.lower(): + return 'availabilityZoneMappings' in location + + return None diff --git a/src/zones/azext_zones/_params.py b/src/zones/azext_zones/_params.py new file mode 100644 index 00000000000..ecbb5c24fb3 --- /dev/null +++ b/src/zones/azext_zones/_params.py @@ -0,0 +1,15 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long + +from azure.cli.core.commands.parameters import get_three_state_flag + + +def load_arguments(self, _): + + with self.argument_context('zones validate') as c: + c.argument('resource_group_names', options_list=['--resource-groups', '-g'], help='Name of the resource groups, comma separated.', required=False) + c.argument('tags', options_list=['--tags'], help='Filter resources based on tags.', required=False) + c.argument('omit_dependent', options_list=['--omit-dependent'], help='Omit dependent resources from validation.', arg_type=get_three_state_flag(), required=False) diff --git a/src/zones/azext_zones/_resourceTypeValidation.py b/src/zones/azext_zones/_resourceTypeValidation.py new file mode 100644 index 00000000000..1da8408736e --- /dev/null +++ b/src/zones/azext_zones/_resourceTypeValidation.py @@ -0,0 +1,52 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from abc import ABC, abstractmethod +from enum import Enum + +resource_type_validators = {} + + +# This is the decorator to register resource type validators: +def register_resource_type(resourceType): + def decorator(cls): + resource_type_validators[resourceType] = cls + return cls + return decorator + + +# This is the factory class to get the appropriate validator based on resource type: +def getResourceTypeValidator(resourceType): + validator_class = resource_type_validators.get(resourceType) + if validator_class: + return validator_class() + return None + + +# This is the base class for all resource type validators: +class ResourceTypeValidator(ABC): # pylint: disable=too-few-public-methods` + @abstractmethod + def validate(self): + pass + + +class ZoneRedundancyValidationResult(Enum): + Unknown = 1 # Unable to verify status + Yes = 2 # Resource is configured for zone redundancy + Always = 3 # Resource is always zone redundant + No = 4 # Resource is not configured for zone redundancy + Never = 5 # Resource cannot be configured for zone redundancy + Dependent = 6 # Resource is zone redundant if parent or related resource is zone redundant + NoZonesInRegion = 7 # Resource is not zone redundant because the region does not support zones + + @staticmethod + def to_string(value): + try: + # Attempt to create an enum member from the value + result = ZoneRedundancyValidationResult(value) + return result.name + except ValueError: + # If the value doesn't correspond to any enum member, return "Unknown" + return "Unknown" diff --git a/src/zones/azext_zones/_validators.py b/src/zones/azext_zones/_validators.py new file mode 100644 index 00000000000..f43bd3d1642 --- /dev/null +++ b/src/zones/azext_zones/_validators.py @@ -0,0 +1,36 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import re + + +def validate_command_args(namespace): + # Validate resource group input: + if not is_valid_resource_group_list(namespace.resource_group_names): + raise ValueError( + "Resource Groups must be single resource group name or a comma-separated list of valid resource groups" + ) + + # Validate tags input: + if not is_valid_tags_list(namespace.tags): + raise ValueError( + "Tags must be a comma-separated list of key-value pairs in the format 'key=value'" + ) + + +def is_valid_resource_group_list(input_string): + if input_string is None: + return True + pattern = r"^(?!.*\.\.)(?!.*\.$)[\w\-\.\(\)]{1,90}$" + names = [name.strip() for name in input_string.split(",")] + return all(re.match(pattern, name, re.IGNORECASE) for name in names) + + +def is_valid_tags_list(input_string): + if input_string is None: + return True + tag_pattern = r"^[^=,\s][^=,]{0,510}=[^=,]{0,256}$" + tags = [tag.strip() for tag in input_string.split(",")] + return all(re.match(tag_pattern, tag) for tag in tags) diff --git a/src/zones/azext_zones/azext_metadata.json b/src/zones/azext_zones/azext_metadata.json new file mode 100644 index 00000000000..55c81bf3328 --- /dev/null +++ b/src/zones/azext_zones/azext_metadata.json @@ -0,0 +1,4 @@ +{ + "azext.isPreview": true, + "azext.minCliCoreVersion": "2.0.67" +} \ No newline at end of file diff --git a/src/zones/azext_zones/commands.py b/src/zones/azext_zones/commands.py new file mode 100644 index 00000000000..4dc590b584d --- /dev/null +++ b/src/zones/azext_zones/commands.py @@ -0,0 +1,13 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +# pylint: disable=line-too-long +from azext_zones._client_factory import cf_zones +from azext_zones._validators import validate_command_args + + +def load_command_table(self, _): + with self.command_group('zones', client_factory=cf_zones, is_preview=True) as g: + g.custom_command('validate', 'validate_zones', validator=validate_command_args) diff --git a/src/zones/azext_zones/custom.py b/src/zones/azext_zones/custom.py new file mode 100644 index 00000000000..5799577f86f --- /dev/null +++ b/src/zones/azext_zones/custom.py @@ -0,0 +1,79 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from knack.log import get_logger +from ._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult +from ._argHelper import build_arg_query, execute_arg_query +from ._locationDataHelper import LocationDataHelper + +__logger = get_logger(__name__) + + +def validate_zones(client, cmd, omit_dependent, resource_group_names, tags): + # Build the ARG query to retrieve resources + query = build_arg_query(resource_group_names, tags) + __logger.debug("Built ARG Query: %s", query) + + # Retrieve the list of resources to validate + resources = execute_arg_query(client, query, None, 0, None, None, False, None) + + # Run validation on the retrieved resources + validation_results = validate_resources(cmd, resources, omit_dependent) + + return validation_results + + +def validate_resources(cmd, resources, omit_dependent=False): + resource_results = [] + if resources['count'] == 0: + errMsg = ("No resources found with the supplied resource group and tag filters, validation could not be run.") + __logger.error(errMsg) + + # Get the location data we'll use to validate the resources + try: + location_data_helper = LocationDataHelper(cmd) + location_data_helper.fetch_location_data() + except Exception as e: # pylint: disable=broad-except + __logger.debug("An error occurred when fetching location data: %s", e) + __logger.warning("An error occurred when fetching location data. \ + Please manually validate if your region supports zones.") + + # Loop through the resources and validate each one + for resource in resources['data']: + resourceProvider = resource['type'].split('/')[0] + region = resource['location'] + zrStatus = None + + # If the region does not have zones, we need to look no further + # If we were unable to fetch location data before, this will return None and it will fall into the else logic + regionHasZones = location_data_helper.region_has_zones(region) + if not regionHasZones: + zrStatus = ZoneRedundancyValidationResult.NoZonesInRegion + else: + validator = getResourceTypeValidator(resourceProvider) + if validator is None: + zrStatus = ZoneRedundancyValidationResult.Unknown + else: + try: + zrStatus = validator.validate(resource) + except KeyError as e: + __logger.warning("KeyError when validating %s: %s\n \ + Please check the resource manually.", resource.get('name', ''), e) + zrStatus = ZoneRedundancyValidationResult.Unknown + except Exception as e: # pylint: disable=broad-except + __logger.warning("An error occurred when validating %s: %s\n \ + Please check the resource manually.", resource.get('name', ''), e) + zrStatus = ZoneRedundancyValidationResult.Unknown + + if zrStatus is not ZoneRedundancyValidationResult.Dependent or not omit_dependent: + resource_result = {} + resource_result['name'] = resource['name'] + resource_result['location'] = resource['location'] + resource_result['resourceGroup'] = resource['resourceGroup'] + resource_result['resourceType'] = resource['type'] + resource_result['zoneRedundant'] = ZoneRedundancyValidationResult.to_string(zrStatus) + resource_results.append(resource_result) + + return resource_results diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_apimanagement.py b/src/zones/azext_zones/resource_type_validators/microsoft_apimanagement.py new file mode 100644 index 00000000000..d0fcb47b7da --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_apimanagement.py @@ -0,0 +1,39 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.apimanagement") +class microsoft_apimanagement: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_apimanagement") + _logger.debug( + "Validating Microsoft.apimanagement resource type: %s", resourceSubType + ) + + # API Management Gateways + if resourceSubType == "gateways": + # ZR state of the gateway is defined on the service level + return ZoneRedundancyValidationResult.Dependent + + if resourceSubType == "service": + # API Management instances are zone redundant if they are premium and have more than one zone + # https://learn.microsoft.com/azure/api-management/high-availability#availability-zones + zones = resource.get("zones") or [] + if len(zones) > 1 and resource["sku"]["name"] == "Premium": + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_app.py b/src/zones/azext_zones/resource_type_validators/microsoft_app.py new file mode 100644 index 00000000000..a6f4ef31622 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_app.py @@ -0,0 +1,38 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.app") +class microsoft_app: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_app") + _logger.debug("Validating Microsoft.app resource type: %s", resourceSubType) + + # Container Apps + if resourceSubType == "containerapps": + # Container apps are zone redundant if they are hosted on a + # zone redundant managedEnvironment + return ZoneRedundancyValidationResult.Dependent + + # Container Apps Environments + if resourceSubType == "managedenvironments": + # Managed Environments are zone redundant if the zoneRedundant property is set to true + # https://learn.microsoft.com/azure/reliability/reliability-azure-container-apps#availability-zone-support + if resource["properties"].get("zoneRedundant", {}) is True: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_authorization.py b/src/zones/azext_zones/resource_type_validators/microsoft_authorization.py new file mode 100644 index 00000000000..580523c065b --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_authorization.py @@ -0,0 +1,26 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.authorization") +class microsoft_authorization: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_authorization") + _logger.debug( + "Validating Microsoft.authorization resource type: %s", resourceSubType + ) + + # authorization resources are always zone redundant + return ZoneRedundancyValidationResult.Always diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_automation.py b/src/zones/azext_zones/resource_type_validators/microsoft_automation.py new file mode 100644 index 00000000000..62e9e281ec6 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_automation.py @@ -0,0 +1,27 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.automation") +class microsoft_automation: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_automation") + _logger.debug( + "Validating Microsoft.automation resource type: %s", resourceSubType + ) + + # Automation accounts are zone redundant by default + # https://learn.microsoft.com/azure/automation/automation-availability-zones + return ZoneRedundancyValidationResult.Always diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_botservice.py b/src/zones/azext_zones/resource_type_validators/microsoft_botservice.py new file mode 100644 index 00000000000..edac1eb6f07 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_botservice.py @@ -0,0 +1,39 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.botservice") +class microsoft_botservice: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_botservice") + _logger.debug( + "Validating Microsoft.botservice resource type: %s", resourceSubType + ) + + # Bot Services + if resourceSubType == "botservices": + # Bot services are ZR only in west europe and + # only if they are configured as a regional (not global) bot. + if resource["location"] == "westeurope": + # https://learn.microsoft.com/azure/reliability/reliability-bot + _logger.warning( + "Your bot service resource in westeurope may be zone redundant, \ + but only if it's configured as a regional (not global) bot. Please check manually." + ) + else: + # Bot services cannot be ZR in any other region + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_cache.py b/src/zones/azext_zones/resource_type_validators/microsoft_cache.py new file mode 100644 index 00000000000..1e11e4df040 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_cache.py @@ -0,0 +1,41 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.cache") +class microsoft_cache: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_cache") + _logger.debug("Validating Microsoft.cache resource type: %s", resourceSubType) + + # Redis + if resourceSubType == "redis": + # Redis caches are zone redundant if they are premium SKU and have more than one zone set + # https://learn.microsoft.com/azure/azure-cache-for-redis/cache-high-availability#zone-redundancy + zones = resource.get("zones") or [] + if len(zones) > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # Redis Enterprise + if resourceSubType == "redisenterprise": + zones = resource.get("zones") or [] + if len(zones) > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_cdn.py b/src/zones/azext_zones/resource_type_validators/microsoft_cdn.py new file mode 100644 index 00000000000..cf5d35c4960 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_cdn.py @@ -0,0 +1,24 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.cdn") +class microsoft_cdn: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_cdn") + _logger.debug("Validating Microsoft.cdn resource type: %s", resourceSubType) + + # Cdn profiles are a global service and are zone redundant by default + return ZoneRedundancyValidationResult.Always diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_chaos.py b/src/zones/azext_zones/resource_type_validators/microsoft_chaos.py new file mode 100644 index 00000000000..9bb8c1cb196 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_chaos.py @@ -0,0 +1,25 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.chaos") +class microsoft_chaos: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_chaos") + _logger.debug("Validating Microsoft.chaos resource type: %s", resourceSubType) + + # chaos profiles are always zone redundant + # https://learn.microsoft.com/azure/reliability/reliability-chaos-studio#availability-zone-support + return ZoneRedundancyValidationResult.Always diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_compute.py b/src/zones/azext_zones/resource_type_validators/microsoft_compute.py new file mode 100644 index 00000000000..c0fbe8423a0 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_compute.py @@ -0,0 +1,55 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.compute") +class microsoft_compute: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_compute") + _logger.debug("Validating Microsoft.Compute resource type: %s", resourceSubType) + + # Disks + if resourceSubType == "disks": + zones = resource.get("zones") or [] + if len(zones) > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # VM ScaleSets + if resourceSubType == "virtualmachinescalesets": + # VMSS is ZR if deployed to more than one zone + zones = resource.get("zones") or [] + if len(zones) > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # Virtual Machines + if resourceSubType == "virtualmachines": + # VM is ZR if deployed to more than one zone + zones = resource.get("zones") or [] + if len(zones) > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # VM Extensions + if resourceSubType == "virtualmachines/extensions": + # VM extensions are zone redundant if the VM they are attached + # to is zone redundant + return ZoneRedundancyValidationResult.Dependent + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_containerinstance.py b/src/zones/azext_zones/resource_type_validators/microsoft_containerinstance.py new file mode 100644 index 00000000000..4e6b296d2dc --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_containerinstance.py @@ -0,0 +1,31 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.containerinstance") +class microsoft_containerinstance: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_containerinstance") + _logger.debug( + "Validating Microsoft.containerinstance resource type: %s", resourceSubType + ) + + # Container Instances + if resourceSubType == "containergroups": + # Container groups of container instances are zonal resources, so they are never zone redundant + # https://learn.microsoft.com/azure/reliability/reliability-containers#availability-zone-support + return ZoneRedundancyValidationResult.Never + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_containerregistry.py b/src/zones/azext_zones/resource_type_validators/microsoft_containerregistry.py new file mode 100644 index 00000000000..0c860e17177 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_containerregistry.py @@ -0,0 +1,41 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.containerregistry") +class microsoft_containerregistry: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_containerregistry") + _logger.debug( + "Validating Microsoft.containerregistry resource type: %s", resourceSubType + ) + + # Container Registry + if resourceSubType == "registries": + # Container registries are zone redundant if the setting enabled + # https://learn.microsoft.com/azure/container-registry/zone-redundancy + if resource["properties"]["zoneRedundancy"] == "Enabled": + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # Registry Regional Replications + if resourceSubType == "registries/replications": + if resource["properties"]["zoneRedundancy"] == "Enabled": + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_containerservice.py b/src/zones/azext_zones/resource_type_validators/microsoft_containerservice.py new file mode 100644 index 00000000000..aba6bda5db7 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_containerservice.py @@ -0,0 +1,41 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.containerservice") +class microsoft_containerservice: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_containerservice") + _logger.debug( + "Validating Microsoft.containerservice resource type: %s", resourceSubType + ) + + # AKS Clusters + if resourceSubType == "managedclusters": + # AKS clusters are zone redundant if the node pools are spread across multiple zones + # Zone Redundancy on AKS involves a lot of configuration steps, testing is required beyond this script. + # https://learn.microsoft.com/azure/aks/availability-zones-overview + poolZones = ( + resource["properties"]["agentPoolProfiles"][0].get("availabilityZones") + or [] + ) + poolZoneCount = len(poolZones) + return ( + ZoneRedundancyValidationResult.Yes + if poolZoneCount > 1 + else ZoneRedundancyValidationResult.No + ) + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_dashboard.py b/src/zones/azext_zones/resource_type_validators/microsoft_dashboard.py new file mode 100644 index 00000000000..66f6255eeea --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_dashboard.py @@ -0,0 +1,33 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.dashboard") +class microsoft_dashboard: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_dashboard") + _logger.debug( + "Validating Microsoft.dashboard resource type: %s", resourceSubType + ) + + # Azure Managed Grafana + if resourceSubType == "grafana": + zr = resource.get("properties", {}).get("zoneRedundancy", "") + if zr == "Enabled": + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_dbformysql.py b/src/zones/azext_zones/resource_type_validators/microsoft_dbformysql.py new file mode 100644 index 00000000000..b6f071666ff --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_dbformysql.py @@ -0,0 +1,37 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.dbformysql") +class microsoft_mysql: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_dbformysql") + _logger.debug("Validating Microsoft.mysql resource type: %s", resourceSubType) + + # Azure Database for MySQL + if resourceSubType == "flexibleservers": + haConfig = ( + resource["properties"].get("highAvailability", {}).get("mode", {}) + ) + if haConfig == "ZoneRedundant": + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # Azure Database for MySQL Single Servers + if resourceSubType == "servers": + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_dbforpostgresql.py b/src/zones/azext_zones/resource_type_validators/microsoft_dbforpostgresql.py new file mode 100644 index 00000000000..7d7b7d2558c --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_dbforpostgresql.py @@ -0,0 +1,41 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.dbforpostgresql") +class microsoft_dbforpostgresql: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_dbforpostgresql") + _logger.debug( + "Validating Microsoft.dbforpostgresql resource type: %s", resourceSubType + ) + + # PostgreSQL Flexible Servers + if resourceSubType == "flexibleservers": + if ( + resource["properties"].get("highAvailability", {}).get("mode", {}) + == "ZoneRedundant" + ): + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # PostgreSQL Single Servers + if resourceSubType == "servers": + # Zone redundancy is not supported for PostgreSQL Single Servers + # https://learn.microsoft.com/azure/reliability/reliability-postgresql-flexible-server + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_documentdb.py b/src/zones/azext_zones/resource_type_validators/microsoft_documentdb.py new file mode 100644 index 00000000000..187c96fa648 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_documentdb.py @@ -0,0 +1,44 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.documentdb") +class microsoft_documentdb: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_documentdb") + _logger.debug( + "Validating Microsoft.documentdb resource type: %s", resourceSubType + ) + + # CosmosDB SQL API Accounts + if resourceSubType == "databaseaccounts": + # https://learn.microsoft.com/en-us/azure/reliability/reliability-cosmos-db-nosql + # CosmosDB databases are zone redundant if then have the + # setting enabled on the region + if resource["properties"]["locations"][0]["isZoneRedundant"]: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # CosmosDB MongoDB API Accounts + if resourceSubType == "mongoClusters": + # https://learn.microsoft.com/azure/reliability/reliability-cosmos-mongodb#availability-zone-support + highAvailability = resource["properties"].get("highAvailability", "") + if highAvailability.get("targetMode", "") == "ZoneRedundantPreferred": + return ZoneRedundancyValidationResult.Yes + else: + ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_eventgrid.py b/src/zones/azext_zones/resource_type_validators/microsoft_eventgrid.py new file mode 100644 index 00000000000..2328b960624 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_eventgrid.py @@ -0,0 +1,28 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.eventgrid") +class microsoft_eventgrid: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_eventgrid") + _logger.debug( + "Validating Microsoft.eventgrid resource type: %s", resourceSubType + ) + + # EventGrid resources are zone redundant by default + # https://learn.microsoft.com/azure/reliability/reliability-event-grid#availability-zone-support + # return ZoneRedundancyValidationResult.Always + return ZoneRedundancyValidationResult.Always diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_eventhub.py b/src/zones/azext_zones/resource_type_validators/microsoft_eventhub.py new file mode 100644 index 00000000000..448ba1a3fb5 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_eventhub.py @@ -0,0 +1,27 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.eventhub") +class microsoft_eventhub: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_eventhub") + _logger.debug( + "Validating Microsoft.eventhub resource type: %s", resourceSubType + ) + + # If you create an Event Hubs namespace in a region that supports availability zones, zone redundancy is automatically enabled. + # https://learn.microsoft.com/azure/reliability/reliability-event-hubs#availability-zone-support + return ZoneRedundancyValidationResult.Always diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_hdinsight.py b/src/zones/azext_zones/resource_type_validators/microsoft_hdinsight.py new file mode 100644 index 00000000000..2692248a4ac --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_hdinsight.py @@ -0,0 +1,26 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.hdinsight") +class microsoft_hdinsight: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_hdinsight") + _logger.debug( + "Validating Microsoft.hdinsight resource type: %s", resourceSubType + ) + + # HDInsight clusters are zonal resources. They exist in a single zone. + return ZoneRedundancyValidationResult.Never diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_insights.py b/src/zones/azext_zones/resource_type_validators/microsoft_insights.py new file mode 100644 index 00000000000..0c76089dace --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_insights.py @@ -0,0 +1,26 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.insights") +class microsoft_insights: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_insights") + _logger.debug( + "Validating Microsoft.insights resource type: %s", resourceSubType + ) + + # insights resources are zone redundant by default + return ZoneRedundancyValidationResult.Always diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_iothub.py b/src/zones/azext_zones/resource_type_validators/microsoft_iothub.py new file mode 100644 index 00000000000..241c1513c22 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_iothub.py @@ -0,0 +1,25 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.iothub") +class microsoft_iothub: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_iothub") + _logger.debug("Validating Microsoft.iothub resource type: %s", resourceSubType) + + # Zone Redundancy is enabled by default for IoT Hubs + # https://learn.microsoft.com/azure/iot-hub/iot-hub-ha-dr#availability-zones + return ZoneRedundancyValidationResult.Always diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_keyvault.py b/src/zones/azext_zones/resource_type_validators/microsoft_keyvault.py new file mode 100644 index 00000000000..938bb6358e7 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_keyvault.py @@ -0,0 +1,31 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.keyvault") +class microsoft_keyvault: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_keyvault") + _logger.debug( + "Validating Microsoft.keyvault resource type: %s", resourceSubType + ) + + # Key Vaults + if resourceSubType == "vaults": + # Key vaults are zone redundant by default + # https://learn.microsoft.com/azure/key-vault/general/disaster-recovery-guidance#failover-across-regions + return ZoneRedundancyValidationResult.Always + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_kusto.py b/src/zones/azext_zones/resource_type_validators/microsoft_kusto.py new file mode 100644 index 00000000000..9774672b4cd --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_kusto.py @@ -0,0 +1,34 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.kusto") +class microsoft_kusto: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_kusto") + _logger.debug("Validating Microsoft.kusto resource type: %s", resourceSubType) + + # Kusto Clusters + if resourceSubType == "clusters": + # AKS clusters are zone redundant if the node pools are spread across multiple zones + # Zone Redundancy on AKS involves a lot of configuration steps, testing is required beyond this script. + # https://learn.microsoft.com/azure/aks/availability-zones-overview + zones = resource.get("zones") or [] + if len(zones) > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_loadtestservice.py b/src/zones/azext_zones/resource_type_validators/microsoft_loadtestservice.py new file mode 100644 index 00000000000..64b15779a02 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_loadtestservice.py @@ -0,0 +1,26 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.loadtestservice") +class microsoft_loadtestservice: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_loadtestservice") + _logger.debug( + "Validating Microsoft.loadtestservice resource type: %s", resourceSubType + ) + + # loadtestservice resources are never zone redundant + return ZoneRedundancyValidationResult.Never diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_machinelearningservices.py b/src/zones/azext_zones/resource_type_validators/microsoft_machinelearningservices.py new file mode 100644 index 00000000000..2f29049dbc0 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_machinelearningservices.py @@ -0,0 +1,30 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.machinelearningservices") +class microsoft_machinelearningservices: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_machinelearningservices") + _logger.debug( + "Validating Microsoft.machinelearningservices resource type: %s", + resourceSubType, + ) + + # Azure Machine Learning + if resourceSubType == "workspaces": + return ZoneRedundancyValidationResult.Never + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_managedidentity.py b/src/zones/azext_zones/resource_type_validators/microsoft_managedidentity.py new file mode 100644 index 00000000000..16e7a8c839c --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_managedidentity.py @@ -0,0 +1,26 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.managedidentity") +class microsoft_managedidentity: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_managedidentity") + _logger.debug( + "Validating Microsoft.managedidentity resource type: %s", resourceSubType + ) + + # managedidentity resources are zone redundant by default + return ZoneRedundancyValidationResult.Always diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_maps.py b/src/zones/azext_zones/resource_type_validators/microsoft_maps.py new file mode 100644 index 00000000000..2bc634375e2 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_maps.py @@ -0,0 +1,24 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.maps") +class microsoft_maps: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_maps") + _logger.debug("Validating Microsoft.maps resource type: %s", resourceSubType) + + # maps resources are zone redundant by default + return ZoneRedundancyValidationResult.Always diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_network.py b/src/zones/azext_zones/resource_type_validators/microsoft_network.py new file mode 100644 index 00000000000..54793d8638a --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_network.py @@ -0,0 +1,127 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.network") +class microsoft_network: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_network") + _logger.debug("Validating Microsoft.Network resource type: %s", resourceSubType) + + # Application Gateways + if resourceSubType == "applicationgateways": + zones = resource.get("zones") or [] + if len(zones) > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # Azure Firewalls + if resourceSubType == "azurefirewalls": + zones = resource.get("zones") or [] + if len(zones) > 1 and resource["sku"]["capacity"] > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # Network Connections + if resourceSubType == "connections": + # Network connections depend on the configuration of the + # Virtual Network Gateway + return ZoneRedundancyValidationResult.Dependent + + # DNS Zones + if resourceSubType == "dnszones": + # Azure DNS is a global service, zone redundant by default + return ZoneRedundancyValidationResult.Always + + # Front Doors + if resourceSubType == "frontdoors": + # Front Door is a global resources and always zone redundant + return ZoneRedundancyValidationResult.Always + + # Load Balancers + if resourceSubType == "loadbalancers": + frontend_ip_configs = ( + resource["properties"].get("frontendIPConfigurations") or [] + ) + zones = frontend_ip_configs[0].get("zones") or [] + if len(zones) > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # Local Network Gateways + if resourceSubType == "localnetworkgateways": + # Local network gateways depend on the configuration of the VPN + # Gateway + return ZoneRedundancyValidationResult.Dependent + + # Network Interfaces + if resourceSubType == "networkinterfaces": + # Network interfaces are in the zone of the virtual machines + # they are attached to + return ZoneRedundancyValidationResult.Dependent + + # Network Security Groups + if resourceSubType == "networksecuritygroups": + return ZoneRedundancyValidationResult.Always + + # Network Watchers, flowslogs, packetcaptures + if ( + resourceSubType == "networkwatchers" + or resourceSubType == "networkwatchers/flowlogs" + or resourceSubType == "networkwatchers/packetcaptures" + ): + # Network watchers are zone redundant by default + return ZoneRedundancyValidationResult.Always + + # Private DNS Zones + if resourceSubType == "privatednszones": + # Private DNS zones are zone redundant by default + # https://learn.microsoft.com/azure/dns/private-dns-resiliency + return ZoneRedundancyValidationResult.Always + + # Private DNS Zone Virtual Network Links + if resourceSubType == "privatednszones/virtualnetworklinks": + return ZoneRedundancyValidationResult.Always + + # Private Endpoints + if resourceSubType == "privateendpoints": + return ZoneRedundancyValidationResult.Always + + # Public IP Addresses + if resourceSubType == "publicipaddresses": + zones = resource.get("zones") or [] + if resource["sku"]["name"] in ["Standard"] and len(zones) > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # Virtual Networks + if resourceSubType == "virtualnetworks": + # Virtual networks span all availability zones in a region. + # https://learn.microsoft.com/azure/virtual-network/virtual-networks-overview#virtual-networks-and-availability-zones + return ZoneRedundancyValidationResult.Always + + # Virtual Network Gateways + if resourceSubType == "virtualnetworkgateways": + sku = resource["properties"]["sku"]["name"] + if sku.endswith("AZ"): + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_operationalinsights.py b/src/zones/azext_zones/resource_type_validators/microsoft_operationalinsights.py new file mode 100644 index 00000000000..606c94d44dc --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_operationalinsights.py @@ -0,0 +1,34 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.operationalinsights") +class microsoft_operationalinsights: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_operationalinsights") + _logger.debug( + "Validating Microsoft.operationalinsights resource type: %s", + resourceSubType, + ) + + # Operational Insights workspaces + if resourceSubType == "workspaces": + # Operational Insights workspaces are zone redundant by default, + # Note: Operational Insights workspaces are zone redundant by + # default only in some regions. Check + # https://learn.microsoft.com/azure/azure-monitor/logs/availability-zones. + return ZoneRedundancyValidationResult.Always + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_recoveryservices.py b/src/zones/azext_zones/resource_type_validators/microsoft_recoveryservices.py new file mode 100644 index 00000000000..35fe1a4eae2 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_recoveryservices.py @@ -0,0 +1,39 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.recoveryservices") +class microsoft_recoveryservices: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_recoveryservices") + _logger.debug( + "Validating Microsoft.recoveryservices resource type: %s", resourceSubType + ) + + # Recovery Services vaults + if resourceSubType == "vaults": + # https://learn.microsoft.com/azure/reliability/reliability-backup#availability-zone-support + # Recovery Services vaults are zone redundant if the storage + # redundancy was set to ZoneRedundant + return ( + ZoneRedundancyValidationResult.Yes + if resource["properties"]["redundancySettings"][ + "standardTierStorageRedundancy" + ] + == "ZoneRedundant" + else ZoneRedundancyValidationResult.No + ) + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_search.py b/src/zones/azext_zones/resource_type_validators/microsoft_search.py new file mode 100644 index 00000000000..126cb7b89f4 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_search.py @@ -0,0 +1,34 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.search") +class microsoft_search: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_search") + _logger.debug("Validating Microsoft.search resource type: %s", resourceSubType) + + # Search Services + if resourceSubType == "searchservices": + # Standard or higher tiers in supported regions are zone redundant if the replica count is greater than 1. + # https://learn.microsoft.com/azure/search/search-reliability#availability-zone-support + sku = resource["sku"]["name"] or "" + replicaCount = resource["properties"].get("replicaCount", 0) + if sku not in ["Free", "Basic"] and replicaCount > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_servicebus.py b/src/zones/azext_zones/resource_type_validators/microsoft_servicebus.py new file mode 100644 index 00000000000..30915e3d40d --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_servicebus.py @@ -0,0 +1,30 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.servicebus") +class microsoft_servicebus: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_servicebus") + _logger.debug( + "Validating Microsoft.servicebus resource type: %s", resourceSubType + ) + + # Service Bus Namespaces + if resourceSubType == "namespaces": + # servicebus namespaces are always zone redundant + return ZoneRedundancyValidationResult.Always + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_signalrservice.py b/src/zones/azext_zones/resource_type_validators/microsoft_signalrservice.py new file mode 100644 index 00000000000..3e319b9ab78 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_signalrservice.py @@ -0,0 +1,34 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.signalrservice") +class microsoft_signalrservice: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_signalrservice") + _logger.debug( + "Validating Microsoft.signalrservice resource type: %s", resourceSubType + ) + + # SignalR Service + if resourceSubType == "signalr": + # SignalR is zone redundant by default on premium tiers + # https://learn.microsoft.com/azure/azure-signalr/availability-zones + if resource["sku"]["name"] == "Premium": + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_sql.py b/src/zones/azext_zones/resource_type_validators/microsoft_sql.py new file mode 100644 index 00000000000..a327228d6a1 --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_sql.py @@ -0,0 +1,52 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.sql") +class microsoft_sql: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_sql") + _logger.debug("Validating Microsoft.sql resource type: %s", resourceSubType) + + # SQL Databases + if resourceSubType == "servers/databases": + # https://learn.microsoft.com/azure/azure-sql/database/high-availability-sla-local-zone-redundancy#high-availability-through-zone-redundancy + if resource["properties"]["zoneRedundant"]: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # SQL Servers + if resourceSubType == "servers": + # Zone Redundancy for SQL is set at the database level, see + # above + return ZoneRedundancyValidationResult.Dependent + + # SQL Managed Instances + if resourceSubType == "managedinstances": + # SQL MI can be zone redundant if this has been enabled on the resource + # https://learn.microsoft.com/azure/azure-sql/managed-instance/high-availability-sla-local-zone-redundancy?view=azuresql#zone-redundant-availability + if resource["properties"].get("zoneRedundant", {}) is True: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # SQL Managed Instance Pools + if resourceSubType == "instancepools": + # Instance pools depend on the managed instances within them to + # be zone redundant + return ZoneRedundancyValidationResult.Dependent + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_storage.py b/src/zones/azext_zones/resource_type_validators/microsoft_storage.py new file mode 100644 index 00000000000..d40390b21ff --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_storage.py @@ -0,0 +1,32 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.storage") +class microsoft_storage: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_storage") + _logger.debug("Validating Microsoft.Storage resource type: %s", resourceSubType) + + # Storage accounts + if resourceSubType == "storageaccounts": + # Storage accounts are zone redundant if they are in the ZRS + # SKU + if resource["sku"]["name"] == "Standard_ZRS": + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/resource_type_validators/microsoft_web.py b/src/zones/azext_zones/resource_type_validators/microsoft_web.py new file mode 100644 index 00000000000..c1fed9c949e --- /dev/null +++ b/src/zones/azext_zones/resource_type_validators/microsoft_web.py @@ -0,0 +1,53 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from .._resourceTypeValidation import ( + ZoneRedundancyValidationResult, + register_resource_type, +) +from knack.log import get_logger + + +@register_resource_type("microsoft.web") +class microsoft_web: + @staticmethod + def validate(resource): + resourceType = resource["type"] + resourceSubType = resourceType[resourceType.index("/") + 1:] + + _logger = get_logger("microsoft_web") + _logger.debug("Validating Microsoft.web resource type: %s", resourceSubType) + + # App Service Plans + if resourceSubType == "serverfarms": + # App Service Plans are zone redundant if they have zone redundancy enabled and have more than one instance + # https://learn.microsoft.com/azure/reliability/reliability-app-service?pivots=free-shared-basic#availability-zone-support + zrEnabled = resource["properties"].get("zoneRedundant", False) + instanceCount = resource["sku"].get("capacity", 0) + if zrEnabled and instanceCount > 1: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # App Services + if resourceSubType == "sites": + # Web Apps are zone redundant if they are hosted on a zone + # redundant App Service Plan + return ZoneRedundancyValidationResult.Dependent + + # Static Web Apps Hosting Environments + if resourceSubType == "hostingenvironments": + zrStatus = resource["properties"].get("zoneRedundant", False) + if zrStatus: + return ZoneRedundancyValidationResult.Yes + else: + return ZoneRedundancyValidationResult.No + + # Static Web Apps + if resourceSubType == "staticsites": + # Static Web Apps are always zone redundant + return ZoneRedundancyValidationResult.Always + + return ZoneRedundancyValidationResult.Unknown diff --git a/src/zones/azext_zones/tests/__init__.py b/src/zones/azext_zones/tests/__init__.py new file mode 100644 index 00000000000..2dcf9bb68b3 --- /dev/null +++ b/src/zones/azext_zones/tests/__init__.py @@ -0,0 +1,5 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/__init__.py b/src/zones/azext_zones/tests/latest/__init__.py new file mode 100644 index 00000000000..2dcf9bb68b3 --- /dev/null +++ b/src/zones/azext_zones/tests/latest/__init__.py @@ -0,0 +1,5 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for +# license information. +# ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_apimanagement.py b/src/zones/azext_zones/tests/latest/test_microsoft_apimanagement.py new file mode 100644 index 00000000000..0c74eb7e18e --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_apimanagement.py @@ -0,0 +1,57 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_apimanagement(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.apimanagement/service", + "sku": { + "name": "Premium", + "capacity": 3 + }, + "zones": [ + "1", + "2", + "3" + ] + } + + resource_nonzr = \ + { + "type": "microsoft.apimanagement/service", + "sku": { + "name": "Premium", + "capacity": 1 + }, + "tags": {}, + "zones": None + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_apimanagement, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_app.py b/src/zones/azext_zones/tests/latest/test_microsoft_app.py new file mode 100644 index 00000000000..d6353142e6f --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_app.py @@ -0,0 +1,50 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_app(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.app/managedenvironments", + "resourceGroup": "testResourceGroup", + "properties": { + "zoneRedundant": True, + }, + } + + resource_nonzr = \ + { + "type": "microsoft.app/managedenvironments", + "resourceGroup": "testResourceGroup", + "properties": { + "zoneRedundant": False, + }, + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_app, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_cache.py b/src/zones/azext_zones/tests/latest/test_microsoft_cache.py new file mode 100644 index 00000000000..a1fdf170d8a --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_cache.py @@ -0,0 +1,48 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_cache(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.cache/redis", + "zones": [ + "1", + "2", + "3" + ] + } + + resource_nonzr = \ + { + "type": "microsoft.cache/redis", + "zones": None + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_cache, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_compute.py b/src/zones/azext_zones/tests/latest/test_microsoft_compute.py new file mode 100644 index 00000000000..f472ca720c3 --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_compute.py @@ -0,0 +1,100 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_app(ScenarioTest): + + resource_disk_zr = \ + { + "type": "microsoft.compute/disks", + "zones": [ + "1", + "2", + "3" + ] + } + + resource_disk_nonzr = \ + { + "type": "microsoft.compute/disks", + "zones": None + } + + resource_vmss_zr = \ + { + "type": "microsoft.compute/virtualmachinescalesets", + "zones": [ + "1", + "2", + "3" + ] + } + + resource_vmss_nonzr = \ + { + "type": "microsoft.compute/virtualmachinescalesets", + "zones": None + } + + resource_vm_zr = \ + { + "type": "microsoft.compute/virtualmachines", + "zones": [ + "1", + "2", + "3" + ] + } + + resource_vm_nonzr = \ + { + "type": "microsoft.compute/virtualmachines", + "zones": None + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_app, cls).setUpClass() + resourceProvider = cls.resource_disk_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_disk_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_disk_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_disk_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_disk_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) + + def test_vmss_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_vmss_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_vmss_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_vmss_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) + + def test_vm_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_vm_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_vm_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_vm_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_containerregistry.py b/src/zones/azext_zones/tests/latest/test_microsoft_containerregistry.py new file mode 100644 index 00000000000..b6f2995777f --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_containerregistry.py @@ -0,0 +1,49 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_containerregistry(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.containerregistry/registries", + "properties": { + "zoneRedundancy": "Enabled" + } + } + + + resource_nonzr = \ + { + "type": "microsoft.containerregistry/registries", + "properties": { + "zoneRedundancy": "Disabled" + } + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_containerregistry, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_containerservice.py b/src/zones/azext_zones/tests/latest/test_microsoft_containerservice.py new file mode 100644 index 00000000000..597a36bcc13 --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_containerservice.py @@ -0,0 +1,58 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_containerservice(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.containerservice/managedclusters", + "properties": { + "agentPoolProfiles": [ + { + "availabilityZones": [ + "1", + "2", + "3" + ] + } + ] + } + } + + resource_nonzr = \ + { + "type": "microsoft.containerservice/managedclusters", + "properties": { + "agentPoolProfiles": [ + {} + ] + } + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_containerservice, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_dashboard.py b/src/zones/azext_zones/tests/latest/test_microsoft_dashboard.py new file mode 100644 index 00000000000..c8f689d6475 --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_dashboard.py @@ -0,0 +1,48 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_dashboard(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.dashboard/grafana", + "properties": { + "zoneRedundancy": 'Enabled' + } + } + + resource_nonzr = \ + { + "type": "microsoft.dashboard/grafana", + "properties": { + "zoneRedundancy": 'Disabled' + } + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_dashboard, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_dbformysql.py b/src/zones/azext_zones/tests/latest/test_microsoft_dbformysql.py new file mode 100644 index 00000000000..0263c06c822 --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_dbformysql.py @@ -0,0 +1,52 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_dbformysql(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.dbformysql/flexibleservers", + "properties": { + "highAvailability": { + "mode": "ZoneRedundant" + } + } + } + + resource_nonzr = \ + { + "type": "microsoft.dbformysql/flexibleservers", + "properties": { + "highAvailability": { + "mode": "Disabled" + } + } + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_dbformysql, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_dbforpostgresql.py b/src/zones/azext_zones/tests/latest/test_microsoft_dbforpostgresql.py new file mode 100644 index 00000000000..ebf0ddfbef0 --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_dbforpostgresql.py @@ -0,0 +1,53 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_dbforpostgresql(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.dbforpostgresql/flexibleservers", + "properties": { + "highAvailability": { + "mode": "ZoneRedundant" + } + } + } + + resource_nonzr = \ + { + "type": "microsoft.dbforpostgresql/flexibleservers", + "properties": { + "highAvailability": { + "state": "NotEnabled", + "mode": "Disabled" + } + } + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_dbforpostgresql, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_documentdb.py b/src/zones/azext_zones/tests/latest/test_microsoft_documentdb.py new file mode 100644 index 00000000000..88521131a55 --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_documentdb.py @@ -0,0 +1,56 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_documentdb(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.documentdb/databaseaccounts", + "properties": { + "locations": [ + { + "isZoneRedundant": True + } + ] + } + } + + resource_nonzr = \ + { + "type": "microsoft.documentdb/databaseaccounts", + "properties": { + "locations": [ + { + "isZoneRedundant": False + } + ] + } + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_documentdb, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_kusto.py b/src/zones/azext_zones/tests/latest/test_microsoft_kusto.py new file mode 100644 index 00000000000..80bd6980e10 --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_kusto.py @@ -0,0 +1,48 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_kusto(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.kusto/clusters", + "zones": [ + "1", + "3", + "2" + ] + } + + resource_nonzr = \ + { + "type": "microsoft.kusto/clusters", + "zones": None + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_kusto, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_network.py b/src/zones/azext_zones/tests/latest/test_microsoft_network.py new file mode 100644 index 00000000000..2d4021ad9cb --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_network.py @@ -0,0 +1,179 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_network(ScenarioTest): + + resource_applicationgateways_zr = \ + { + "type": "microsoft.network/applicationgateways", + "zones": [ + "1", + "2", + "3" + ] + } + + resource_applicationgateways_nonzr = \ + { + "type": "microsoft.network/applicationgateways", + "zones": None + } + + resource_azurefirewalls_zr = \ + { + "type": "microsoft.network/azurefirewalls", + "sku": { + "capacity": 3 + }, + "zones": [ + "1", + "2", + "3" + ] + } + + resource_azurefirewalls_nonzr = \ + { + "type": "microsoft.network/azurefirewalls", + "sku": { + "capacity": 1 + }, + "zones": None + } + + resource_loadbalancers_zr = \ + { + "type": "microsoft.network/loadbalancers", + "properties": { + "frontendIPConfigurations": [ + { + "zones": [ + "1", + "2", + "3" + ] + } + ] + } + } + resource_loadbalancers_nonzr = \ + { + "type": "microsoft.network/loadbalancers", + "properties": { + "frontendIPConfigurations": [ + { + "zones": None + } + ] + } + } + + resource_publicipaddresses_zr = \ + { + "type": "microsoft.network/publicipaddresses", + "sku": { + "name": "Standard" + }, + "zones": [ + "1", + "2", + "3" + ] + } + + resource_publicipaddresses_nonzr = \ + { + "type": "microsoft.network/publicipaddresses", + "sku": { + "name": "Basic" + }, + "zones": None + } + + resource_virtualnetworkgateways_zr = \ + { + "type": "microsoft.network/virtualnetworkgateways", + "properties": { + "sku": { + "name": "VpnGw2AZ", + } + } + } + + resource_virtualnetworkgateways_nonzr = \ + { + "type": "microsoft.network/virtualnetworkgateways", + "properties": { + "sku": { + "name": "VpnGw2", + } + } + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_network, cls).setUpClass() + resourceProvider = cls.resource_applicationgateways_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_applicationgateways_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_applicationgateways_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_applicationgateways_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_applicationgateways_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) + + def test_azurefirewalls_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_azurefirewalls_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_azurefirewalls_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_azurefirewalls_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) + + def test_loadbalancers_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_loadbalancers_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_loadbalancers_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_loadbalancers_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) + + def test_publicipaddresses_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_publicipaddresses_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_publicipaddresses_nzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_publicipaddresses_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) + + def test_virtualnetworkgateways_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_virtualnetworkgateways_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_virtualnetworkgateways_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_virtualnetworkgateways_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_recoveryservices.py b/src/zones/azext_zones/tests/latest/test_microsoft_recoveryservices.py new file mode 100644 index 00000000000..8d1d2b3741f --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_recoveryservices.py @@ -0,0 +1,52 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_recoveryservices(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.recoveryservices/vaults", + "properties": { + "redundancySettings": { + "standardTierStorageRedundancy": "ZoneRedundant", + } + } + } + + resource_nonzr = \ + { + "type": "microsoft.recoveryservices/vaults", + "properties": { + "redundancySettings": { + "standardTierStorageRedundancy": "LocallyRedundant", + } + } + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_recoveryservices, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_search.py b/src/zones/azext_zones/tests/latest/test_microsoft_search.py new file mode 100644 index 00000000000..0a06600b19f --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_search.py @@ -0,0 +1,54 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_search(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.search/searchservices", + "sku": { + "name": "standard" + }, + "properties": { + "replicaCount": 3 + } + } + + resource_nonzr = \ + { + "type": "microsoft.search/searchservices", + "sku": { + "name": "standard" + }, + "properties": { + "replicaCount": 1 + } + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_search, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_signalrservice.py b/src/zones/azext_zones/tests/latest/test_microsoft_signalrservice.py new file mode 100644 index 00000000000..99a0712d2c6 --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_signalrservice.py @@ -0,0 +1,50 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_signalrservice(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.signalrservice/signalr", + "sku": { + "name": "Premium" + }, + "zones": None + } + + resource_nonzr = \ + { + "type": "microsoft.signalrservice/signalr", + "sku": { + "name": "Standard" + }, + "zones": None + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_signalrservice, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_sql.py b/src/zones/azext_zones/tests/latest/test_microsoft_sql.py new file mode 100644 index 00000000000..f24c1946bf5 --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_sql.py @@ -0,0 +1,48 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_sql(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.sql/servers/databases", + "properties": { + "zoneRedundant": True, + }, + } + + resource_nonzr = \ + { + "type": "microsoft.sql/servers/databases", + "properties": { + "zoneRedundant": False, + }, + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_sql, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_storage.py b/src/zones/azext_zones/tests/latest/test_microsoft_storage.py new file mode 100644 index 00000000000..ae8f1ddb1a3 --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_storage.py @@ -0,0 +1,48 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_storage(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.storage/storageaccounts", + "sku": { + "name": "Standard_ZRS", + } + } + + resource_nonzr = \ + { + "type": "microsoft.storage/storageaccounts", + "sku": { + "name": "Standard_LRS", + } + } + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_storage, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/tests/latest/test_microsoft_web.py b/src/zones/azext_zones/tests/latest/test_microsoft_web.py new file mode 100644 index 00000000000..cc59ced625e --- /dev/null +++ b/src/zones/azext_zones/tests/latest/test_microsoft_web.py @@ -0,0 +1,55 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest) +from ..._resourceTypeValidation import getResourceTypeValidator, ZoneRedundancyValidationResult + + +class test_microsoft_web(ScenarioTest): + + resource_zr = \ + { + "type": "microsoft.web/serverfarms", + "sku": { + "capacity": 2 + }, + "properties": { + "zoneRedundant": True + } + } + + resource_nonzr = \ + { + "type": "microsoft.web/serverfarms", + "sku": { + "capacity": 1 + }, + "properties": { + "zoneRedundant": False + } + } + + + validator = None + + @classmethod + def setUpClass(cls): + super(test_microsoft_web, cls).setUpClass() + resourceProvider = cls.resource_zr['type'].split('/')[0] + cls.validator = getResourceTypeValidator(resourceProvider) + + + def test_zr(self): + # Test for zone redundancy scenario + zrResult = self.validator.validate(self.resource_zr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.Yes) + + def test_nonzr(self): + # Test for non-zone redundancy scenario + zrResult = self.validator.validate(self.resource_nonzr) + self.assertEqual(zrResult, ZoneRedundancyValidationResult.No) \ No newline at end of file diff --git a/src/zones/azext_zones/vendored_sdks/__init__.py b/src/zones/azext_zones/vendored_sdks/__init__.py new file mode 100644 index 00000000000..34913fb394d --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/__init__.py @@ -0,0 +1,4 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/__init__.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/__init__.py new file mode 100644 index 00000000000..7df07d41873 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/__init__.py @@ -0,0 +1,19 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._resource_graph_client import ResourceGraphClient +from ._version import VERSION + +__version__ = VERSION +__all__ = ['ResourceGraphClient'] + +try: + from ._patch import patch_sdk # type: ignore + patch_sdk() +except ImportError: + pass diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/_configuration.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/_configuration.py new file mode 100644 index 00000000000..c883e46cc0e --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/_configuration.py @@ -0,0 +1,70 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies +from azure.mgmt.core.policies import ARMHttpLoggingPolicy + +from ._version import VERSION + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any + + from azure.core.credentials import TokenCredential + + +class ResourceGraphClientConfiguration(Configuration): + """Configuration for ResourceGraphClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param credential: Credential needed for the client to connect to Azure. + :type credential: ~azure.core.credentials.TokenCredential + :param subscription_id: The Azure subscription Id. + :type subscription_id: str + """ + + def __init__( + self, + credential, # type: "TokenCredential" + subscription_id, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + if credential is None: + raise ValueError("Parameter 'credential' must not be None.") + if subscription_id is None: + raise ValueError("Parameter 'subscription_id' must not be None.") + super(ResourceGraphClientConfiguration, self).__init__(**kwargs) + + self.credential = credential + self.subscription_id = subscription_id + self.credential_scopes = kwargs.pop('credential_scopes', ['https://management.azure.com/.default']) + kwargs.setdefault('sdk_moniker', 'mgmt-resourcegraph/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs # type: Any + ): + # type: (...) -> None + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or ARMHttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.RetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.RedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') + if self.credential and not self.authentication_policy: + self.authentication_policy = policies.BearerTokenCredentialPolicy(self.credential, *self.credential_scopes, **kwargs) diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/_metadata.json b/src/zones/azext_zones/vendored_sdks/resourcegraph/_metadata.json new file mode 100644 index 00000000000..6b4eca7d986 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/_metadata.json @@ -0,0 +1,110 @@ +{ + "chosen_version": "", + "total_api_version_list": ["2018-09-01-preview", "2020-04-01-preview", "2020-09-01-preview", "2021-03-01"], + "client": { + "name": "ResourceGraphClient", + "filename": "_resource_graph_client", + "description": "Azure Resource Graph API Reference.", + "base_url": "\u0027https://management.azure.com\u0027", + "custom_base_url": null, + "azure_arm": true, + "has_lro_operations": false, + "client_side_validation": true + }, + "global_parameters": { + "sync": { + "credential": { + "signature": "credential, # type: \"TokenCredential\"", + "description": "Credential needed for the client to connect to Azure.", + "docstring_type": "~azure.core.credentials.TokenCredential", + "required": true + }, + "subscription_id": { + "signature": "subscription_id, # type: str", + "description": "The Azure subscription Id.", + "docstring_type": "str", + "required": true + } + }, + "async": { + "credential": { + "signature": "credential, # type: \"AsyncTokenCredential\"", + "description": "Credential needed for the client to connect to Azure.", + "docstring_type": "~azure.core.credentials_async.AsyncTokenCredential", + "required": true + }, + "subscription_id": { + "signature": "subscription_id, # type: str", + "description": "The Azure subscription Id.", + "docstring_type": "str", + "required": true + } + }, + "constant": { + }, + "call": "credential, subscription_id" + }, + "config": { + "credential": true, + "credential_scopes": ["https://management.azure.com/.default"], + "credential_default_policy_type": "BearerTokenCredentialPolicy", + "credential_default_policy_type_has_async_version": true, + "credential_key_header_name": null + }, + "operation_groups": { + "operations": "Operations", + "graph_query": "GraphQueryOperations" + }, + "operation_mixins": { + "resource_changes" : { + "sync": { + "signature": "def resource_changes(\n self,\n parameters, # type: \"_models.ResourceChangesRequestParameters\"\n **kwargs # type: Any\n):\n", + "doc": "\"\"\"List changes to a resource for a given time interval.\n\n:param parameters: the parameters for this request for changes.\n:type parameters: ~azure.mgmt.resourcegraph.models.ResourceChangesRequestParameters\n:keyword callable cls: A custom type or function that will be passed the direct response\n:return: ResourceChangeList, or the result of cls(response)\n:rtype: ~azure.mgmt.resourcegraph.models.ResourceChangeList\n:raises: ~azure.core.exceptions.HttpResponseError\n\"\"\"" + }, + "async": { + "coroutine": true, + "signature": "async def resource_changes(\n self,\n parameters: \"_models.ResourceChangesRequestParameters\",\n **kwargs\n) -\u003e \"_models.ResourceChangeList\":\n", + "doc": "\"\"\"List changes to a resource for a given time interval.\n\n:param parameters: the parameters for this request for changes.\n:type parameters: ~azure.mgmt.resourcegraph.models.ResourceChangesRequestParameters\n:keyword callable cls: A custom type or function that will be passed the direct response\n:return: ResourceChangeList, or the result of cls(response)\n:rtype: ~azure.mgmt.resourcegraph.models.ResourceChangeList\n:raises: ~azure.core.exceptions.HttpResponseError\n\"\"\"" + }, + "call": "parameters" + }, + "resource_change_details" : { + "sync": { + "signature": "def resource_change_details(\n self,\n parameters, # type: \"_models.ResourceChangeDetailsRequestParameters\"\n **kwargs # type: Any\n):\n", + "doc": "\"\"\"Get resource change details.\n\n:param parameters: The parameters for this request for resource change details.\n:type parameters: ~azure.mgmt.resourcegraph.models.ResourceChangeDetailsRequestParameters\n:keyword callable cls: A custom type or function that will be passed the direct response\n:return: list of ResourceChangeData, or the result of cls(response)\n:rtype: list[~azure.mgmt.resourcegraph.models.ResourceChangeData]\n:raises: ~azure.core.exceptions.HttpResponseError\n\"\"\"" + }, + "async": { + "coroutine": true, + "signature": "async def resource_change_details(\n self,\n parameters: \"_models.ResourceChangeDetailsRequestParameters\",\n **kwargs\n) -\u003e List[\"_models.ResourceChangeData\"]:\n", + "doc": "\"\"\"Get resource change details.\n\n:param parameters: The parameters for this request for resource change details.\n:type parameters: ~azure.mgmt.resourcegraph.models.ResourceChangeDetailsRequestParameters\n:keyword callable cls: A custom type or function that will be passed the direct response\n:return: list of ResourceChangeData, or the result of cls(response)\n:rtype: list[~azure.mgmt.resourcegraph.models.ResourceChangeData]\n:raises: ~azure.core.exceptions.HttpResponseError\n\"\"\"" + }, + "call": "parameters" + }, + "resources" : { + "sync": { + "signature": "def resources(\n self,\n query, # type: \"_models.QueryRequest\"\n **kwargs # type: Any\n):\n", + "doc": "\"\"\"Queries the resources managed by Azure Resource Manager for scopes specified in the request.\n\n:param query: Request specifying query and its options.\n:type query: ~azure.mgmt.resourcegraph.models.QueryRequest\n:keyword callable cls: A custom type or function that will be passed the direct response\n:return: QueryResponse, or the result of cls(response)\n:rtype: ~azure.mgmt.resourcegraph.models.QueryResponse\n:raises: ~azure.core.exceptions.HttpResponseError\n\"\"\"" + }, + "async": { + "coroutine": true, + "signature": "async def resources(\n self,\n query: \"_models.QueryRequest\",\n **kwargs\n) -\u003e \"_models.QueryResponse\":\n", + "doc": "\"\"\"Queries the resources managed by Azure Resource Manager for scopes specified in the request.\n\n:param query: Request specifying query and its options.\n:type query: ~azure.mgmt.resourcegraph.models.QueryRequest\n:keyword callable cls: A custom type or function that will be passed the direct response\n:return: QueryResponse, or the result of cls(response)\n:rtype: ~azure.mgmt.resourcegraph.models.QueryResponse\n:raises: ~azure.core.exceptions.HttpResponseError\n\"\"\"" + }, + "call": "query" + }, + "resources_history" : { + "sync": { + "signature": "def resources_history(\n self,\n request, # type: \"_models.ResourcesHistoryRequest\"\n **kwargs # type: Any\n):\n", + "doc": "\"\"\"List all snapshots of a resource for a given time interval.\n\n:param request:\n:type request: ~azure.mgmt.resourcegraph.models.ResourcesHistoryRequest\n:keyword callable cls: A custom type or function that will be passed the direct response\n:return: object, or the result of cls(response)\n:rtype: object\n:raises: ~azure.core.exceptions.HttpResponseError\n\"\"\"" + }, + "async": { + "coroutine": true, + "signature": "async def resources_history(\n self,\n request: \"_models.ResourcesHistoryRequest\",\n **kwargs\n) -\u003e object:\n", + "doc": "\"\"\"List all snapshots of a resource for a given time interval.\n\n:param request:\n:type request: ~azure.mgmt.resourcegraph.models.ResourcesHistoryRequest\n:keyword callable cls: A custom type or function that will be passed the direct response\n:return: object, or the result of cls(response)\n:rtype: object\n:raises: ~azure.core.exceptions.HttpResponseError\n\"\"\"" + }, + "call": "request" + } + }, + "sync_imports": "{\"regular\": {\"azurecore\": {\"azure.core.exceptions\": [\"ClientAuthenticationError\", \"HttpResponseError\", \"ResourceExistsError\", \"ResourceNotFoundError\", \"map_error\"], \"azure.mgmt.core.exceptions\": [\"ARMErrorFormat\"], \"azure.core.pipeline\": [\"PipelineResponse\"], \"azure.core.pipeline.transport\": [\"HttpRequest\", \"HttpResponse\"]}, \"stdlib\": {\"warnings\": [null]}}, \"conditional\": {\"stdlib\": {\"typing\": [\"Any\", \"Callable\", \"Dict\", \"Generic\", \"List\", \"Optional\", \"TypeVar\"]}}}", + "async_imports": "{\"regular\": {\"azurecore\": {\"azure.core.exceptions\": [\"ClientAuthenticationError\", \"HttpResponseError\", \"ResourceExistsError\", \"ResourceNotFoundError\", \"map_error\"], \"azure.mgmt.core.exceptions\": [\"ARMErrorFormat\"], \"azure.core.pipeline\": [\"PipelineResponse\"], \"azure.core.pipeline.transport\": [\"AsyncHttpResponse\", \"HttpRequest\"]}, \"stdlib\": {\"warnings\": [null]}}, \"conditional\": {\"stdlib\": {\"typing\": [\"Any\", \"Callable\", \"Dict\", \"Generic\", \"List\", \"Optional\", \"TypeVar\"]}}}" +} \ No newline at end of file diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/_resource_graph_client.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/_resource_graph_client.py new file mode 100644 index 00000000000..cc07fe0aad8 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/_resource_graph_client.py @@ -0,0 +1,74 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING + +from azure.mgmt.core import ARMPipelineClient +from msrest import Deserializer, Serializer + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Optional + + from azure.core.credentials import TokenCredential + +from ._configuration import ResourceGraphClientConfiguration +from .operations import ResourceGraphClientOperationsMixin +from .operations import Operations +from .operations import GraphQueryOperations +from . import models + + +class ResourceGraphClient(ResourceGraphClientOperationsMixin): + """Azure Resource Graph API Reference. + + :ivar operations: Operations operations + :vartype operations: azure.mgmt.resourcegraph.operations.Operations + :ivar graph_query: GraphQueryOperations operations + :vartype graph_query: azure.mgmt.resourcegraph.operations.GraphQueryOperations + :param credential: Credential needed for the client to connect to Azure. + :type credential: ~azure.core.credentials.TokenCredential + :param subscription_id: The Azure subscription Id. + :type subscription_id: str + :param str base_url: Service URL + """ + + def __init__( + self, + credential, # type: "TokenCredential" + subscription_id, # type: str + base_url=None, # type: Optional[str] + **kwargs # type: Any + ): + # type: (...) -> None + if not base_url: + base_url = 'https://management.azure.com' + self._config = ResourceGraphClientConfiguration(credential, subscription_id, **kwargs) + self._client = ARMPipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + + self.operations = Operations( + self._client, self._config, self._serialize, self._deserialize) + self.graph_query = GraphQueryOperations( + self._client, self._config, self._serialize, self._deserialize) + + def close(self): + # type: () -> None + self._client.close() + + def __enter__(self): + # type: () -> ResourceGraphClient + self._client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._client.__exit__(*exc_details) diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/_version.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/_version.py new file mode 100644 index 00000000000..c1257f7f4e1 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/_version.py @@ -0,0 +1,9 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +VERSION = "10.1.0" diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/__init__.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/__init__.py new file mode 100644 index 00000000000..ec4d8b3a968 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/__init__.py @@ -0,0 +1,10 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._resource_graph_client import ResourceGraphClient +__all__ = ['ResourceGraphClient'] diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/_configuration.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/_configuration.py new file mode 100644 index 00000000000..2d5c6b5e5ae --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/_configuration.py @@ -0,0 +1,66 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any, TYPE_CHECKING + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies +from azure.mgmt.core.policies import ARMHttpLoggingPolicy + +from .._version import VERSION + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from azure.core.credentials_async import AsyncTokenCredential + + +class ResourceGraphClientConfiguration(Configuration): + """Configuration for ResourceGraphClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param credential: Credential needed for the client to connect to Azure. + :type credential: ~azure.core.credentials_async.AsyncTokenCredential + :param subscription_id: The Azure subscription Id. + :type subscription_id: str + """ + + def __init__( + self, + credential: "AsyncTokenCredential", + subscription_id: str, + **kwargs: Any + ) -> None: + if credential is None: + raise ValueError("Parameter 'credential' must not be None.") + if subscription_id is None: + raise ValueError("Parameter 'subscription_id' must not be None.") + super(ResourceGraphClientConfiguration, self).__init__(**kwargs) + + self.credential = credential + self.subscription_id = subscription_id + self.credential_scopes = kwargs.pop('credential_scopes', ['https://management.azure.com/.default']) + kwargs.setdefault('sdk_moniker', 'mgmt-resourcegraph/{}'.format(VERSION)) + self._configure(**kwargs) + + def _configure( + self, + **kwargs: Any + ) -> None: + self.user_agent_policy = kwargs.get('user_agent_policy') or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get('headers_policy') or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get('proxy_policy') or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get('logging_policy') or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get('http_logging_policy') or ARMHttpLoggingPolicy(**kwargs) + self.retry_policy = kwargs.get('retry_policy') or policies.AsyncRetryPolicy(**kwargs) + self.custom_hook_policy = kwargs.get('custom_hook_policy') or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get('redirect_policy') or policies.AsyncRedirectPolicy(**kwargs) + self.authentication_policy = kwargs.get('authentication_policy') + if self.credential and not self.authentication_policy: + self.authentication_policy = policies.AsyncBearerTokenCredentialPolicy(self.credential, *self.credential_scopes, **kwargs) diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/_resource_graph_client.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/_resource_graph_client.py new file mode 100644 index 00000000000..6e7e1328b17 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/_resource_graph_client.py @@ -0,0 +1,68 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any, Optional, TYPE_CHECKING + +from azure.mgmt.core import AsyncARMPipelineClient +from msrest import Deserializer, Serializer + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from azure.core.credentials_async import AsyncTokenCredential + +from ._configuration import ResourceGraphClientConfiguration +from .operations import ResourceGraphClientOperationsMixin +from .operations import Operations +from .operations import GraphQueryOperations +from .. import models + + +class ResourceGraphClient(ResourceGraphClientOperationsMixin): + """Azure Resource Graph API Reference. + + :ivar operations: Operations operations + :vartype operations: azure.mgmt.resourcegraph.aio.operations.Operations + :ivar graph_query: GraphQueryOperations operations + :vartype graph_query: azure.mgmt.resourcegraph.aio.operations.GraphQueryOperations + :param credential: Credential needed for the client to connect to Azure. + :type credential: ~azure.core.credentials_async.AsyncTokenCredential + :param subscription_id: The Azure subscription Id. + :type subscription_id: str + :param str base_url: Service URL + """ + + def __init__( + self, + credential: "AsyncTokenCredential", + subscription_id: str, + base_url: Optional[str] = None, + **kwargs: Any + ) -> None: + if not base_url: + base_url = 'https://management.azure.com' + self._config = ResourceGraphClientConfiguration(credential, subscription_id, **kwargs) + self._client = AsyncARMPipelineClient(base_url=base_url, config=self._config, **kwargs) + + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + self._serialize = Serializer(client_models) + self._deserialize = Deserializer(client_models) + + self.operations = Operations( + self._client, self._config, self._serialize, self._deserialize) + self.graph_query = GraphQueryOperations( + self._client, self._config, self._serialize, self._deserialize) + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> "ResourceGraphClient": + await self._client.__aenter__() + return self + + async def __aexit__(self, *exc_details) -> None: + await self._client.__aexit__(*exc_details) diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/__init__.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/__init__.py new file mode 100644 index 00000000000..21a67ce4dae --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/__init__.py @@ -0,0 +1,17 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._resource_graph_client_operations import ResourceGraphClientOperationsMixin +from ._operations import Operations +from ._graph_query_operations import GraphQueryOperations + +__all__ = [ + 'ResourceGraphClientOperationsMixin', + 'Operations', + 'GraphQueryOperations', +] diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/_graph_query_operations.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/_graph_query_operations.py new file mode 100644 index 00000000000..3f937c41b76 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/_graph_query_operations.py @@ -0,0 +1,362 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, AsyncIterable, Callable, Dict, Generic, Optional, TypeVar +import warnings + +from azure.core.async_paging import AsyncItemPaged, AsyncList +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest +from azure.mgmt.core.exceptions import ARMErrorFormat + +from ... import models as _models + +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class GraphQueryOperations: + """GraphQueryOperations async operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~azure.mgmt.resourcegraph.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = _models + + def __init__(self, client, config, serializer, deserializer) -> None: + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + def list( + self, + resource_group_name: str, + **kwargs + ) -> AsyncIterable["_models.GraphQueryListResult"]: + """Get all graph queries defined within a specified subscription and resource group. + + :param resource_group_name: The name of the resource group. + :type resource_group_name: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: An iterator like instance of either GraphQueryListResult or the result of cls(response) + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.mgmt.resourcegraph.models.GraphQueryListResult] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GraphQueryListResult"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2018-09-01-preview" + accept = "application/json" + + def prepare_request(next_link=None): + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + if not next_link: + # Construct URL + url = self.list.metadata['url'] # type: ignore + path_format_arguments = { + 'subscriptionId': self._serialize.url("self._config.subscription_id", self._config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + else: + url = next_link + query_parameters = {} # type: Dict[str, Any] + request = self._client.get(url, query_parameters, header_parameters) + return request + + async def extract_data(pipeline_response): + deserialized = self._deserialize('GraphQueryListResult', pipeline_response) + list_of_elem = deserialized.value + if cls: + list_of_elem = cls(list_of_elem) + return deserialized.next_link or None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + request = prepare_request(next_link) + + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + error = self._deserialize(_models.GraphQueryError, response) + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + return pipeline_response + + return AsyncItemPaged( + get_next, extract_data + ) + list.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ResourceGraph/queries'} # type: ignore + + async def get( + self, + resource_group_name: str, + resource_name: str, + **kwargs + ) -> "_models.GraphQueryResource": + """Get a single graph query by its resourceName. + + :param resource_group_name: The name of the resource group. + :type resource_group_name: str + :param resource_name: The name of the Graph Query resource. + :type resource_name: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: GraphQueryResource, or the result of cls(response) + :rtype: ~azure.mgmt.resourcegraph.models.GraphQueryResource + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GraphQueryResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2018-09-01-preview" + accept = "application/json" + + # Construct URL + url = self.get.metadata['url'] # type: ignore + path_format_arguments = { + 'subscriptionId': self._serialize.url("self._config.subscription_id", self._config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.GraphQueryError, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('GraphQueryResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + get.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ResourceGraph/queries/{resourceName}'} # type: ignore + + async def delete( + self, + resource_group_name: str, + resource_name: str, + **kwargs + ) -> None: + """Delete a graph query. + + :param resource_group_name: The name of the resource group. + :type resource_group_name: str + :param resource_name: The name of the Graph Query resource. + :type resource_name: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None, or the result of cls(response) + :rtype: None + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2018-09-01-preview" + accept = "application/json" + + # Construct URL + url = self.delete.metadata['url'] # type: ignore + path_format_arguments = { + 'subscriptionId': self._serialize.url("self._config.subscription_id", self._config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.delete(url, query_parameters, header_parameters) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200, 204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.GraphQueryError, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + if cls: + return cls(pipeline_response, None, {}) + + delete.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ResourceGraph/queries/{resourceName}'} # type: ignore + + async def create_or_update( + self, + resource_group_name: str, + resource_name: str, + properties: "_models.GraphQueryResource", + **kwargs + ) -> "_models.GraphQueryResource": + """Create a new graph query. + + :param resource_group_name: The name of the resource group. + :type resource_group_name: str + :param resource_name: The name of the Graph Query resource. + :type resource_name: str + :param properties: Properties that need to be specified to create a new graph query. + :type properties: ~azure.mgmt.resourcegraph.models.GraphQueryResource + :keyword callable cls: A custom type or function that will be passed the direct response + :return: GraphQueryResource, or the result of cls(response) + :rtype: ~azure.mgmt.resourcegraph.models.GraphQueryResource + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GraphQueryResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2018-09-01-preview" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.create_or_update.metadata['url'] # type: ignore + path_format_arguments = { + 'subscriptionId': self._serialize.url("self._config.subscription_id", self._config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(properties, 'GraphQueryResource') + body_content_kwargs['content'] = body_content + request = self._client.put(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.GraphQueryError, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('GraphQueryResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + create_or_update.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ResourceGraph/queries/{resourceName}'} # type: ignore + + async def update( + self, + resource_group_name: str, + resource_name: str, + body: "_models.GraphQueryUpdateParameters", + **kwargs + ) -> "_models.GraphQueryResource": + """Updates a graph query that has already been added. + + :param resource_group_name: The name of the resource group. + :type resource_group_name: str + :param resource_name: The name of the Graph Query resource. + :type resource_name: str + :param body: Properties that need to be specified to create a new graph query. + :type body: ~azure.mgmt.resourcegraph.models.GraphQueryUpdateParameters + :keyword callable cls: A custom type or function that will be passed the direct response + :return: GraphQueryResource, or the result of cls(response) + :rtype: ~azure.mgmt.resourcegraph.models.GraphQueryResource + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GraphQueryResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2018-09-01-preview" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.update.metadata['url'] # type: ignore + path_format_arguments = { + 'subscriptionId': self._serialize.url("self._config.subscription_id", self._config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(body, 'GraphQueryUpdateParameters') + body_content_kwargs['content'] = body_content + request = self._client.patch(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.GraphQueryError, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('GraphQueryResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + update.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ResourceGraph/queries/{resourceName}'} # type: ignore diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/_operations.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/_operations.py new file mode 100644 index 00000000000..d824ff3399e --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/_operations.py @@ -0,0 +1,105 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, AsyncIterable, Callable, Dict, Generic, Optional, TypeVar +import warnings + +from azure.core.async_paging import AsyncItemPaged, AsyncList +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest +from azure.mgmt.core.exceptions import ARMErrorFormat + +from ... import models as _models + +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class Operations: + """Operations async operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~azure.mgmt.resourcegraph.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = _models + + def __init__(self, client, config, serializer, deserializer) -> None: + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + def list( + self, + **kwargs + ) -> AsyncIterable["_models.OperationListResult"]: + """Lists all of the available REST API operations. + + :keyword callable cls: A custom type or function that will be passed the direct response + :return: An iterator like instance of either OperationListResult or the result of cls(response) + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.mgmt.resourcegraph.models.OperationListResult] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.OperationListResult"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-01" + accept = "application/json" + + def prepare_request(next_link=None): + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + if not next_link: + # Construct URL + url = self.list.metadata['url'] # type: ignore + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + else: + url = next_link + query_parameters = {} # type: Dict[str, Any] + request = self._client.get(url, query_parameters, header_parameters) + return request + + async def extract_data(pipeline_response): + deserialized = self._deserialize('OperationListResult', pipeline_response) + list_of_elem = deserialized.value + if cls: + list_of_elem = cls(list_of_elem) + return None, AsyncList(list_of_elem) + + async def get_next(next_link=None): + request = prepare_request(next_link) + + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + error = self._deserialize(_models.ErrorResponse, response) + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + return pipeline_response + + return AsyncItemPaged( + get_next, extract_data + ) + list.metadata = {'url': '/providers/Microsoft.ResourceGraph/operations'} # type: ignore diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/_resource_graph_client_operations.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/_resource_graph_client_operations.py new file mode 100644 index 00000000000..ada2db38932 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/aio/operations/_resource_graph_client_operations.py @@ -0,0 +1,241 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import Any, Callable, Dict, Generic, List, Optional, TypeVar +import warnings + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import AsyncHttpResponse, HttpRequest +from azure.mgmt.core.exceptions import ARMErrorFormat + +from ... import models as _models + +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class ResourceGraphClientOperationsMixin: + + async def resource_changes( + self, + parameters: "_models.ResourceChangesRequestParameters", + **kwargs + ) -> "_models.ResourceChangeList": + """List changes to a resource for a given time interval. + + :param parameters: the parameters for this request for changes. + :type parameters: ~azure.mgmt.resourcegraph.models.ResourceChangesRequestParameters + :keyword callable cls: A custom type or function that will be passed the direct response + :return: ResourceChangeList, or the result of cls(response) + :rtype: ~azure.mgmt.resourcegraph.models.ResourceChangeList + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.ResourceChangeList"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2020-09-01-preview" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.resource_changes.metadata['url'] # type: ignore + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(parameters, 'ResourceChangesRequestParameters') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.ErrorResponse, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('ResourceChangeList', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + resource_changes.metadata = {'url': '/providers/Microsoft.ResourceGraph/resourceChanges'} # type: ignore + + async def resource_change_details( + self, + parameters: "_models.ResourceChangeDetailsRequestParameters", + **kwargs + ) -> List["_models.ResourceChangeData"]: + """Get resource change details. + + :param parameters: The parameters for this request for resource change details. + :type parameters: ~azure.mgmt.resourcegraph.models.ResourceChangeDetailsRequestParameters + :keyword callable cls: A custom type or function that will be passed the direct response + :return: list of ResourceChangeData, or the result of cls(response) + :rtype: list[~azure.mgmt.resourcegraph.models.ResourceChangeData] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[List["_models.ResourceChangeData"]] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2020-09-01-preview" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.resource_change_details.metadata['url'] # type: ignore + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(parameters, 'ResourceChangeDetailsRequestParameters') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.ErrorResponse, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('[ResourceChangeData]', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + resource_change_details.metadata = {'url': '/providers/Microsoft.ResourceGraph/resourceChangeDetails'} # type: ignore + + async def resources( + self, + query: "_models.QueryRequest", + **kwargs + ) -> "_models.QueryResponse": + """Queries the resources managed by Azure Resource Manager for scopes specified in the request. + + :param query: Request specifying query and its options. + :type query: ~azure.mgmt.resourcegraph.models.QueryRequest + :keyword callable cls: A custom type or function that will be passed the direct response + :return: QueryResponse, or the result of cls(response) + :rtype: ~azure.mgmt.resourcegraph.models.QueryResponse + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.QueryResponse"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-01" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.resources.metadata['url'] # type: ignore + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(query, 'QueryRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.ErrorResponse, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('QueryResponse', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + resources.metadata = {'url': '/providers/Microsoft.ResourceGraph/resources'} # type: ignore + + async def resources_history( + self, + request: "_models.ResourcesHistoryRequest", + **kwargs + ) -> object: + """List all snapshots of a resource for a given time interval. + + :param request: + :type request: ~azure.mgmt.resourcegraph.models.ResourcesHistoryRequest + :keyword callable cls: A custom type or function that will be passed the direct response + :return: object, or the result of cls(response) + :rtype: object + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[object] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2020-04-01-preview" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.resources_history.metadata['url'] # type: ignore + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(request, 'ResourcesHistoryRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.ErrorResponse, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('object', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + resources_history.metadata = {'url': '/providers/Microsoft.ResourceGraph/resourcesHistory'} # type: ignore diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/models/__init__.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/models/__init__.py new file mode 100644 index 00000000000..fa87792c8fd --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/models/__init__.py @@ -0,0 +1,136 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +try: + from ._models_py3 import Column + from ._models_py3 import DateTimeInterval + from ._models_py3 import Error + from ._models_py3 import ErrorDetails + from ._models_py3 import ErrorFieldContract + from ._models_py3 import ErrorResponse + from ._models_py3 import Facet + from ._models_py3 import FacetError + from ._models_py3 import FacetRequest + from ._models_py3 import FacetRequestOptions + from ._models_py3 import FacetResult + from ._models_py3 import GraphQueryError + from ._models_py3 import GraphQueryListResult + from ._models_py3 import GraphQueryResource + from ._models_py3 import GraphQueryUpdateParameters + from ._models_py3 import Operation + from ._models_py3 import OperationDisplay + from ._models_py3 import OperationListResult + from ._models_py3 import QueryRequest + from ._models_py3 import QueryRequestOptions + from ._models_py3 import QueryResponse + from ._models_py3 import Resource + from ._models_py3 import ResourceChangeData + from ._models_py3 import ResourceChangeDataAfterSnapshot + from ._models_py3 import ResourceChangeDataBeforeSnapshot + from ._models_py3 import ResourceChangeDetailsRequestParameters + from ._models_py3 import ResourceChangeList + from ._models_py3 import ResourceChangesRequestParameters + from ._models_py3 import ResourceChangesRequestParametersInterval + from ._models_py3 import ResourcePropertyChange + from ._models_py3 import ResourceSnapshotData + from ._models_py3 import ResourcesHistoryRequest + from ._models_py3 import ResourcesHistoryRequestOptions + from ._models_py3 import Table +except (SyntaxError, ImportError): + from ._models import Column # type: ignore + from ._models import DateTimeInterval # type: ignore + from ._models import Error # type: ignore + from ._models import ErrorDetails # type: ignore + from ._models import ErrorFieldContract # type: ignore + from ._models import ErrorResponse # type: ignore + from ._models import Facet # type: ignore + from ._models import FacetError # type: ignore + from ._models import FacetRequest # type: ignore + from ._models import FacetRequestOptions # type: ignore + from ._models import FacetResult # type: ignore + from ._models import GraphQueryError # type: ignore + from ._models import GraphQueryListResult # type: ignore + from ._models import GraphQueryResource # type: ignore + from ._models import GraphQueryUpdateParameters # type: ignore + from ._models import Operation # type: ignore + from ._models import OperationDisplay # type: ignore + from ._models import OperationListResult # type: ignore + from ._models import QueryRequest # type: ignore + from ._models import QueryRequestOptions # type: ignore + from ._models import QueryResponse # type: ignore + from ._models import Resource # type: ignore + from ._models import ResourceChangeData # type: ignore + from ._models import ResourceChangeDataAfterSnapshot # type: ignore + from ._models import ResourceChangeDataBeforeSnapshot # type: ignore + from ._models import ResourceChangeDetailsRequestParameters # type: ignore + from ._models import ResourceChangeList # type: ignore + from ._models import ResourceChangesRequestParameters # type: ignore + from ._models import ResourceChangesRequestParametersInterval # type: ignore + from ._models import ResourcePropertyChange # type: ignore + from ._models import ResourceSnapshotData # type: ignore + from ._models import ResourcesHistoryRequest # type: ignore + from ._models import ResourcesHistoryRequestOptions # type: ignore + from ._models import Table # type: ignore + +from ._resource_graph_client_enums import ( + ChangeCategory, + ChangeType, + ColumnDataType, + FacetSortOrder, + PropertyChangeType, + ResourcesHistoryRequestOptionsResultFormat, + ResultFormat, + ResultKind, + ResultTruncated, +) + +__all__ = [ + 'Column', + 'DateTimeInterval', + 'Error', + 'ErrorDetails', + 'ErrorFieldContract', + 'ErrorResponse', + 'Facet', + 'FacetError', + 'FacetRequest', + 'FacetRequestOptions', + 'FacetResult', + 'GraphQueryError', + 'GraphQueryListResult', + 'GraphQueryResource', + 'GraphQueryUpdateParameters', + 'Operation', + 'OperationDisplay', + 'OperationListResult', + 'QueryRequest', + 'QueryRequestOptions', + 'QueryResponse', + 'Resource', + 'ResourceChangeData', + 'ResourceChangeDataAfterSnapshot', + 'ResourceChangeDataBeforeSnapshot', + 'ResourceChangeDetailsRequestParameters', + 'ResourceChangeList', + 'ResourceChangesRequestParameters', + 'ResourceChangesRequestParametersInterval', + 'ResourcePropertyChange', + 'ResourceSnapshotData', + 'ResourcesHistoryRequest', + 'ResourcesHistoryRequestOptions', + 'Table', + 'ChangeCategory', + 'ChangeType', + 'ColumnDataType', + 'FacetSortOrder', + 'PropertyChangeType', + 'ResourcesHistoryRequestOptionsResultFormat', + 'ResultFormat', + 'ResultKind', + 'ResultTruncated', +] diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/models/_models.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/models/_models.py new file mode 100644 index 00000000000..3e0f8b2f1c2 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/models/_models.py @@ -0,0 +1,1239 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from azure.core.exceptions import HttpResponseError +import msrest.serialization + + +class Column(msrest.serialization.Model): + """Query result column descriptor. + + All required parameters must be populated in order to send to Azure. + + :param name: Required. Column name. + :type name: str + :param type: Required. Column data type. Possible values include: "string", "integer", + "number", "boolean", "object". + :type type: str or ~azure.mgmt.resourcegraph.models.ColumnDataType + """ + + _validation = { + 'name': {'required': True}, + 'type': {'required': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(Column, self).__init__(**kwargs) + self.name = kwargs['name'] + self.type = kwargs['type'] + + +class DateTimeInterval(msrest.serialization.Model): + """An interval in time specifying the date and time for the inclusive start and exclusive end, i.e. ``[start, end)``. + + All required parameters must be populated in order to send to Azure. + + :param start: Required. A datetime indicating the inclusive/closed start of the time interval, + i.e. ``[``\ **\ ``start``\ **\ ``, end)``. Specifying a ``start`` that occurs chronologically + after ``end`` will result in an error. + :type start: ~datetime.datetime + :param end: Required. A datetime indicating the exclusive/open end of the time interval, i.e. + ``[start,``\ **\ ``end``\ **\ ``)``. Specifying an ``end`` that occurs chronologically before + ``start`` will result in an error. + :type end: ~datetime.datetime + """ + + _validation = { + 'start': {'required': True}, + 'end': {'required': True}, + } + + _attribute_map = { + 'start': {'key': 'start', 'type': 'iso-8601'}, + 'end': {'key': 'end', 'type': 'iso-8601'}, + } + + def __init__( + self, + **kwargs + ): + super(DateTimeInterval, self).__init__(**kwargs) + self.start = kwargs['start'] + self.end = kwargs['end'] + + +class Error(msrest.serialization.Model): + """Error details. + + All required parameters must be populated in order to send to Azure. + + :param code: Required. Error code identifying the specific error. + :type code: str + :param message: Required. A human readable error message. + :type message: str + :param details: Error details. + :type details: list[~azure.mgmt.resourcegraph.models.ErrorDetails] + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'details': {'key': 'details', 'type': '[ErrorDetails]'}, + } + + def __init__( + self, + **kwargs + ): + super(Error, self).__init__(**kwargs) + self.code = kwargs['code'] + self.message = kwargs['message'] + self.details = kwargs.get('details', None) + + +class ErrorDetails(msrest.serialization.Model): + """Error details. + + All required parameters must be populated in order to send to Azure. + + :param additional_properties: Unmatched properties from the message are deserialized to this + collection. + :type additional_properties: dict[str, object] + :param code: Required. Error code identifying the specific error. + :type code: str + :param message: Required. A human readable error message. + :type message: str + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + } + + _attribute_map = { + 'additional_properties': {'key': '', 'type': '{object}'}, + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(ErrorDetails, self).__init__(**kwargs) + self.additional_properties = kwargs.get('additional_properties', None) + self.code = kwargs['code'] + self.message = kwargs['message'] + + +class ErrorFieldContract(msrest.serialization.Model): + """Error Field contract. + + :param code: Property level error code. + :type code: str + :param message: Human-readable representation of property-level error. + :type message: str + :param target: Property name. + :type target: str + """ + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'target': {'key': 'target', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(ErrorFieldContract, self).__init__(**kwargs) + self.code = kwargs.get('code', None) + self.message = kwargs.get('message', None) + self.target = kwargs.get('target', None) + + +class ErrorResponse(msrest.serialization.Model): + """An error response from the API. + + All required parameters must be populated in order to send to Azure. + + :param error: Required. Error information. + :type error: ~azure.mgmt.resourcegraph.models.Error + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + 'error': {'key': 'error', 'type': 'Error'}, + } + + def __init__( + self, + **kwargs + ): + super(ErrorResponse, self).__init__(**kwargs) + self.error = kwargs['error'] + + +class Facet(msrest.serialization.Model): + """A facet containing additional statistics on the response of a query. Can be either FacetResult or FacetError. + + You probably want to use the sub-classes and not this class directly. Known + sub-classes are: FacetError, FacetResult. + + All required parameters must be populated in order to send to Azure. + + :param expression: Required. Facet expression, same as in the corresponding facet request. + :type expression: str + :param result_type: Required. Result type.Constant filled by server. + :type result_type: str + """ + + _validation = { + 'expression': {'required': True}, + 'result_type': {'required': True}, + } + + _attribute_map = { + 'expression': {'key': 'expression', 'type': 'str'}, + 'result_type': {'key': 'resultType', 'type': 'str'}, + } + + _subtype_map = { + 'result_type': {'FacetError': 'FacetError', 'FacetResult': 'FacetResult'} + } + + def __init__( + self, + **kwargs + ): + super(Facet, self).__init__(**kwargs) + self.expression = kwargs['expression'] + self.result_type = None # type: Optional[str] + + +class FacetError(Facet): + """A facet whose execution resulted in an error. + + All required parameters must be populated in order to send to Azure. + + :param expression: Required. Facet expression, same as in the corresponding facet request. + :type expression: str + :param result_type: Required. Result type.Constant filled by server. + :type result_type: str + :param errors: Required. An array containing detected facet errors with details. + :type errors: list[~azure.mgmt.resourcegraph.models.ErrorDetails] + """ + + _validation = { + 'expression': {'required': True}, + 'result_type': {'required': True}, + 'errors': {'required': True}, + } + + _attribute_map = { + 'expression': {'key': 'expression', 'type': 'str'}, + 'result_type': {'key': 'resultType', 'type': 'str'}, + 'errors': {'key': 'errors', 'type': '[ErrorDetails]'}, + } + + def __init__( + self, + **kwargs + ): + super(FacetError, self).__init__(**kwargs) + self.result_type = 'FacetError' # type: str + self.errors = kwargs['errors'] + + +class FacetRequest(msrest.serialization.Model): + """A request to compute additional statistics (facets) over the query results. + + All required parameters must be populated in order to send to Azure. + + :param expression: Required. The column or list of columns to summarize by. + :type expression: str + :param options: The options for facet evaluation. + :type options: ~azure.mgmt.resourcegraph.models.FacetRequestOptions + """ + + _validation = { + 'expression': {'required': True}, + } + + _attribute_map = { + 'expression': {'key': 'expression', 'type': 'str'}, + 'options': {'key': 'options', 'type': 'FacetRequestOptions'}, + } + + def __init__( + self, + **kwargs + ): + super(FacetRequest, self).__init__(**kwargs) + self.expression = kwargs['expression'] + self.options = kwargs.get('options', None) + + +class FacetRequestOptions(msrest.serialization.Model): + """The options for facet evaluation. + + :param sort_by: The column name or query expression to sort on. Defaults to count if not + present. + :type sort_by: str + :param sort_order: The sorting order by the selected column (count by default). Possible values + include: "asc", "desc". Default value: "desc". + :type sort_order: str or ~azure.mgmt.resourcegraph.models.FacetSortOrder + :param filter: Specifies the filter condition for the 'where' clause which will be run on main + query's result, just before the actual faceting. + :type filter: str + :param top: The maximum number of facet rows that should be returned. + :type top: int + """ + + _validation = { + 'top': {'maximum': 1000, 'minimum': 1}, + } + + _attribute_map = { + 'sort_by': {'key': 'sortBy', 'type': 'str'}, + 'sort_order': {'key': 'sortOrder', 'type': 'str'}, + 'filter': {'key': 'filter', 'type': 'str'}, + 'top': {'key': '$top', 'type': 'int'}, + } + + def __init__( + self, + **kwargs + ): + super(FacetRequestOptions, self).__init__(**kwargs) + self.sort_by = kwargs.get('sort_by', None) + self.sort_order = kwargs.get('sort_order', "desc") + self.filter = kwargs.get('filter', None) + self.top = kwargs.get('top', None) + + +class FacetResult(Facet): + """Successfully executed facet containing additional statistics on the response of a query. + + All required parameters must be populated in order to send to Azure. + + :param expression: Required. Facet expression, same as in the corresponding facet request. + :type expression: str + :param result_type: Required. Result type.Constant filled by server. + :type result_type: str + :param total_records: Required. Number of total records in the facet results. + :type total_records: long + :param count: Required. Number of records returned in the facet response. + :type count: int + :param data: Required. A JObject array or Table containing the desired facets. Only present if + the facet is valid. + :type data: object + """ + + _validation = { + 'expression': {'required': True}, + 'result_type': {'required': True}, + 'total_records': {'required': True}, + 'count': {'required': True}, + 'data': {'required': True}, + } + + _attribute_map = { + 'expression': {'key': 'expression', 'type': 'str'}, + 'result_type': {'key': 'resultType', 'type': 'str'}, + 'total_records': {'key': 'totalRecords', 'type': 'long'}, + 'count': {'key': 'count', 'type': 'int'}, + 'data': {'key': 'data', 'type': 'object'}, + } + + def __init__( + self, + **kwargs + ): + super(FacetResult, self).__init__(**kwargs) + self.result_type = 'FacetResult' # type: str + self.total_records = kwargs['total_records'] + self.count = kwargs['count'] + self.data = kwargs['data'] + + +class GraphQueryError(msrest.serialization.Model): + """Error message body that will indicate why the operation failed. + + :param code: Service-defined error code. This code serves as a sub-status for the HTTP error + code specified in the response. + :type code: str + :param message: Human-readable representation of the error. + :type message: str + :param details: The list of invalid fields send in request, in case of validation error. + :type details: list[~azure.mgmt.resourcegraph.models.ErrorFieldContract] + """ + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'details': {'key': 'details', 'type': '[ErrorFieldContract]'}, + } + + def __init__( + self, + **kwargs + ): + super(GraphQueryError, self).__init__(**kwargs) + self.code = kwargs.get('code', None) + self.message = kwargs.get('message', None) + self.details = kwargs.get('details', None) + + +class GraphQueryListResult(msrest.serialization.Model): + """Graph query list result. + + Variables are only populated by the server, and will be ignored when sending a request. + + :param next_link: URL to fetch the next set of queries. + :type next_link: str + :ivar value: An array of graph queries. + :vartype value: list[~azure.mgmt.resourcegraph.models.GraphQueryResource] + """ + + _validation = { + 'value': {'readonly': True}, + } + + _attribute_map = { + 'next_link': {'key': 'nextLink', 'type': 'str'}, + 'value': {'key': 'value', 'type': '[GraphQueryResource]'}, + } + + def __init__( + self, + **kwargs + ): + super(GraphQueryListResult, self).__init__(**kwargs) + self.next_link = kwargs.get('next_link', None) + self.value = None + + +class Resource(msrest.serialization.Model): + """An azure resource object. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar id: Azure resource Id. + :vartype id: str + :ivar name: Azure resource name. This is GUID value. The display name should be assigned within + properties field. + :vartype name: str + :param location: The location of the resource. + :type location: str + :ivar type: Azure resource type. + :vartype type: str + :param etag: This will be used to handle Optimistic Concurrency. If not present, it will always + overwrite the existing resource without checking conflict. + :type etag: str + :param tags: A set of tags. Resource tags. + :type tags: dict[str, str] + """ + + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'location': {'key': 'location', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + 'etag': {'key': 'etag', 'type': 'str'}, + 'tags': {'key': 'tags', 'type': '{str}'}, + } + + def __init__( + self, + **kwargs + ): + super(Resource, self).__init__(**kwargs) + self.id = None + self.name = None + self.location = kwargs.get('location', None) + self.type = None + self.etag = kwargs.get('etag', None) + self.tags = kwargs.get('tags', None) + + +class GraphQueryResource(Resource): + """Graph Query entity definition. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar id: Azure resource Id. + :vartype id: str + :ivar name: Azure resource name. This is GUID value. The display name should be assigned within + properties field. + :vartype name: str + :param location: The location of the resource. + :type location: str + :ivar type: Azure resource type. + :vartype type: str + :param etag: This will be used to handle Optimistic Concurrency. If not present, it will always + overwrite the existing resource without checking conflict. + :type etag: str + :param tags: A set of tags. Resource tags. + :type tags: dict[str, str] + :ivar time_modified: Date and time in UTC of the last modification that was made to this graph + query definition. + :vartype time_modified: ~datetime.datetime + :param description: The description of a graph query. + :type description: str + :param query: KQL query that will be graph. + :type query: str + :ivar result_kind: Enum indicating a type of graph query. Possible values include: "basic". + :vartype result_kind: str or ~azure.mgmt.resourcegraph.models.ResultKind + """ + + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, + 'time_modified': {'readonly': True}, + 'result_kind': {'readonly': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'location': {'key': 'location', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + 'etag': {'key': 'etag', 'type': 'str'}, + 'tags': {'key': 'tags', 'type': '{str}'}, + 'time_modified': {'key': 'properties.timeModified', 'type': 'iso-8601'}, + 'description': {'key': 'properties.description', 'type': 'str'}, + 'query': {'key': 'properties.query', 'type': 'str'}, + 'result_kind': {'key': 'properties.resultKind', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(GraphQueryResource, self).__init__(**kwargs) + self.time_modified = None + self.description = kwargs.get('description', None) + self.query = kwargs.get('query', None) + self.result_kind = None + + +class GraphQueryUpdateParameters(msrest.serialization.Model): + """The parameters that can be provided when updating workbook properties properties. + + :param tags: A set of tags. Resource tags. + :type tags: dict[str, str] + :param etag: This will be used to handle Optimistic Concurrency. If not present, it will always + overwrite the existing resource without checking conflict. + :type etag: str + :param description: The description of a graph query. + :type description: str + :param query: KQL query that will be graph. + :type query: str + """ + + _attribute_map = { + 'tags': {'key': 'tags', 'type': '{str}'}, + 'etag': {'key': 'etag', 'type': 'str'}, + 'description': {'key': 'properties.description', 'type': 'str'}, + 'query': {'key': 'properties.query', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(GraphQueryUpdateParameters, self).__init__(**kwargs) + self.tags = kwargs.get('tags', None) + self.etag = kwargs.get('etag', None) + self.description = kwargs.get('description', None) + self.query = kwargs.get('query', None) + + +class Operation(msrest.serialization.Model): + """Resource Graph REST API operation definition. + + :param name: Operation name: {provider}/{resource}/{operation}. + :type name: str + :param display: Display metadata associated with the operation. + :type display: ~azure.mgmt.resourcegraph.models.OperationDisplay + :param origin: The origin of operations. + :type origin: str + """ + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'display': {'key': 'display', 'type': 'OperationDisplay'}, + 'origin': {'key': 'origin', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(Operation, self).__init__(**kwargs) + self.name = kwargs.get('name', None) + self.display = kwargs.get('display', None) + self.origin = kwargs.get('origin', None) + + +class OperationDisplay(msrest.serialization.Model): + """Display metadata associated with the operation. + + :param provider: Service provider: Microsoft Resource Graph. + :type provider: str + :param resource: Resource on which the operation is performed etc. + :type resource: str + :param operation: Type of operation: get, read, delete, etc. + :type operation: str + :param description: Description for the operation. + :type description: str + """ + + _attribute_map = { + 'provider': {'key': 'provider', 'type': 'str'}, + 'resource': {'key': 'resource', 'type': 'str'}, + 'operation': {'key': 'operation', 'type': 'str'}, + 'description': {'key': 'description', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(OperationDisplay, self).__init__(**kwargs) + self.provider = kwargs.get('provider', None) + self.resource = kwargs.get('resource', None) + self.operation = kwargs.get('operation', None) + self.description = kwargs.get('description', None) + + +class OperationListResult(msrest.serialization.Model): + """Result of the request to list Resource Graph operations. It contains a list of operations and a URL link to get the next set of results. + + :param value: List of Resource Graph operations supported by the Resource Graph resource + provider. + :type value: list[~azure.mgmt.resourcegraph.models.Operation] + """ + + _attribute_map = { + 'value': {'key': 'value', 'type': '[Operation]'}, + } + + def __init__( + self, + **kwargs + ): + super(OperationListResult, self).__init__(**kwargs) + self.value = kwargs.get('value', None) + + +class QueryRequest(msrest.serialization.Model): + """Describes a query to be executed. + + All required parameters must be populated in order to send to Azure. + + :param subscriptions: Azure subscriptions against which to execute the query. + :type subscriptions: list[str] + :param management_groups: Azure management groups against which to execute the query. Example: + [ 'mg1', 'mg2' ]. + :type management_groups: list[str] + :param query: Required. The resources query. + :type query: str + :param options: The query evaluation options. + :type options: ~azure.mgmt.resourcegraph.models.QueryRequestOptions + :param facets: An array of facet requests to be computed against the query result. + :type facets: list[~azure.mgmt.resourcegraph.models.FacetRequest] + """ + + _validation = { + 'query': {'required': True}, + } + + _attribute_map = { + 'subscriptions': {'key': 'subscriptions', 'type': '[str]'}, + 'management_groups': {'key': 'managementGroups', 'type': '[str]'}, + 'query': {'key': 'query', 'type': 'str'}, + 'options': {'key': 'options', 'type': 'QueryRequestOptions'}, + 'facets': {'key': 'facets', 'type': '[FacetRequest]'}, + } + + def __init__( + self, + **kwargs + ): + super(QueryRequest, self).__init__(**kwargs) + self.subscriptions = kwargs.get('subscriptions', None) + self.management_groups = kwargs.get('management_groups', None) + self.query = kwargs['query'] + self.options = kwargs.get('options', None) + self.facets = kwargs.get('facets', None) + + +class QueryRequestOptions(msrest.serialization.Model): + """The options for query evaluation. + + :param skip_token: Continuation token for pagination, capturing the next page size and offset, + as well as the context of the query. + :type skip_token: str + :param top: The maximum number of rows that the query should return. Overrides the page size + when ``$skipToken`` property is present. + :type top: int + :param skip: The number of rows to skip from the beginning of the results. Overrides the next + page offset when ``$skipToken`` property is present. + :type skip: int + :param result_format: Defines in which format query result returned. Possible values include: + "table", "objectArray". Default value: "objectArray". + :type result_format: str or ~azure.mgmt.resourcegraph.models.ResultFormat + :param allow_partial_scopes: Only applicable for tenant and management group level queries to + decide whether to allow partial scopes for result in case the number of subscriptions exceed + allowed limits. + :type allow_partial_scopes: bool + """ + + _validation = { + 'top': {'maximum': 1000, 'minimum': 1}, + 'skip': {'minimum': 0}, + } + + _attribute_map = { + 'skip_token': {'key': '$skipToken', 'type': 'str'}, + 'top': {'key': '$top', 'type': 'int'}, + 'skip': {'key': '$skip', 'type': 'int'}, + 'result_format': {'key': 'resultFormat', 'type': 'str'}, + 'allow_partial_scopes': {'key': 'allowPartialScopes', 'type': 'bool'}, + } + + def __init__( + self, + **kwargs + ): + super(QueryRequestOptions, self).__init__(**kwargs) + self.skip_token = kwargs.get('skip_token', None) + self.top = kwargs.get('top', None) + self.skip = kwargs.get('skip', None) + self.result_format = kwargs.get('result_format', "objectArray") + self.allow_partial_scopes = kwargs.get('allow_partial_scopes', False) + + +class QueryResponse(msrest.serialization.Model): + """Query result. + + All required parameters must be populated in order to send to Azure. + + :param total_records: Required. Number of total records matching the query. + :type total_records: long + :param count: Required. Number of records returned in the current response. In the case of + paging, this is the number of records in the current page. + :type count: long + :param result_truncated: Required. Indicates whether the query results are truncated. Possible + values include: "true", "false". + :type result_truncated: str or ~azure.mgmt.resourcegraph.models.ResultTruncated + :param skip_token: When present, the value can be passed to a subsequent query call (together + with the same query and scopes used in the current request) to retrieve the next page of data. + :type skip_token: str + :param data: Required. Query output in JObject array or Table format. + :type data: object + :param facets: Query facets. + :type facets: list[~azure.mgmt.resourcegraph.models.Facet] + """ + + _validation = { + 'total_records': {'required': True}, + 'count': {'required': True}, + 'result_truncated': {'required': True}, + 'data': {'required': True}, + } + + _attribute_map = { + 'total_records': {'key': 'totalRecords', 'type': 'long'}, + 'count': {'key': 'count', 'type': 'long'}, + 'result_truncated': {'key': 'resultTruncated', 'type': 'str'}, + 'skip_token': {'key': '$skipToken', 'type': 'str'}, + 'data': {'key': 'data', 'type': 'object'}, + 'facets': {'key': 'facets', 'type': '[Facet]'}, + } + + def __init__( + self, + **kwargs + ): + super(QueryResponse, self).__init__(**kwargs) + self.total_records = kwargs['total_records'] + self.count = kwargs['count'] + self.result_truncated = kwargs['result_truncated'] + self.skip_token = kwargs.get('skip_token', None) + self.data = kwargs['data'] + self.facets = kwargs.get('facets', None) + + +class ResourceChangeData(msrest.serialization.Model): + """Data on a specific change, represented by a pair of before and after resource snapshots. + + All required parameters must be populated in order to send to Azure. + + :param resource_id: The resource for a change. + :type resource_id: str + :param change_id: Required. The change ID. Valid and unique within the specified resource only. + :type change_id: str + :param before_snapshot: Required. The snapshot before the change. + :type before_snapshot: ~azure.mgmt.resourcegraph.models.ResourceSnapshotData + :param after_snapshot: Required. The snapshot after the change. + :type after_snapshot: ~azure.mgmt.resourcegraph.models.ResourceSnapshotData + :param change_type: The change type for snapshot. PropertyChanges will be provided in case of + Update change type. Possible values include: "Create", "Update", "Delete". + :type change_type: str or ~azure.mgmt.resourcegraph.models.ChangeType + :param property_changes: An array of resource property change. + :type property_changes: list[~azure.mgmt.resourcegraph.models.ResourcePropertyChange] + """ + + _validation = { + 'change_id': {'required': True}, + 'before_snapshot': {'required': True}, + 'after_snapshot': {'required': True}, + } + + _attribute_map = { + 'resource_id': {'key': 'resourceId', 'type': 'str'}, + 'change_id': {'key': 'changeId', 'type': 'str'}, + 'before_snapshot': {'key': 'beforeSnapshot', 'type': 'ResourceSnapshotData'}, + 'after_snapshot': {'key': 'afterSnapshot', 'type': 'ResourceSnapshotData'}, + 'change_type': {'key': 'changeType', 'type': 'str'}, + 'property_changes': {'key': 'propertyChanges', 'type': '[ResourcePropertyChange]'}, + } + + def __init__( + self, + **kwargs + ): + super(ResourceChangeData, self).__init__(**kwargs) + self.resource_id = kwargs.get('resource_id', None) + self.change_id = kwargs['change_id'] + self.before_snapshot = kwargs['before_snapshot'] + self.after_snapshot = kwargs['after_snapshot'] + self.change_type = kwargs.get('change_type', None) + self.property_changes = kwargs.get('property_changes', None) + + +class ResourceSnapshotData(msrest.serialization.Model): + """Data on a specific resource snapshot. + + All required parameters must be populated in order to send to Azure. + + :param snapshot_id: The ID of the snapshot. + :type snapshot_id: str + :param timestamp: Required. The time when the snapshot was created. + The snapshot timestamp provides an approximation as to when a modification to a resource was + detected. There can be a difference between the actual modification time and the detection + time. This is due to differences in how operations that modify a resource are processed, + versus how operation that record resource snapshots are processed. + :type timestamp: ~datetime.datetime + :param content: The resource snapshot content (in resourceChangeDetails response only). + :type content: object + """ + + _validation = { + 'timestamp': {'required': True}, + } + + _attribute_map = { + 'snapshot_id': {'key': 'snapshotId', 'type': 'str'}, + 'timestamp': {'key': 'timestamp', 'type': 'iso-8601'}, + 'content': {'key': 'content', 'type': 'object'}, + } + + def __init__( + self, + **kwargs + ): + super(ResourceSnapshotData, self).__init__(**kwargs) + self.snapshot_id = kwargs.get('snapshot_id', None) + self.timestamp = kwargs['timestamp'] + self.content = kwargs.get('content', None) + + +class ResourceChangeDataAfterSnapshot(ResourceSnapshotData): + """The snapshot after the change. + + All required parameters must be populated in order to send to Azure. + + :param snapshot_id: The ID of the snapshot. + :type snapshot_id: str + :param timestamp: Required. The time when the snapshot was created. + The snapshot timestamp provides an approximation as to when a modification to a resource was + detected. There can be a difference between the actual modification time and the detection + time. This is due to differences in how operations that modify a resource are processed, + versus how operation that record resource snapshots are processed. + :type timestamp: ~datetime.datetime + :param content: The resource snapshot content (in resourceChangeDetails response only). + :type content: object + """ + + _validation = { + 'timestamp': {'required': True}, + } + + _attribute_map = { + 'snapshot_id': {'key': 'snapshotId', 'type': 'str'}, + 'timestamp': {'key': 'timestamp', 'type': 'iso-8601'}, + 'content': {'key': 'content', 'type': 'object'}, + } + + def __init__( + self, + **kwargs + ): + super(ResourceChangeDataAfterSnapshot, self).__init__(**kwargs) + + +class ResourceChangeDataBeforeSnapshot(ResourceSnapshotData): + """The snapshot before the change. + + All required parameters must be populated in order to send to Azure. + + :param snapshot_id: The ID of the snapshot. + :type snapshot_id: str + :param timestamp: Required. The time when the snapshot was created. + The snapshot timestamp provides an approximation as to when a modification to a resource was + detected. There can be a difference between the actual modification time and the detection + time. This is due to differences in how operations that modify a resource are processed, + versus how operation that record resource snapshots are processed. + :type timestamp: ~datetime.datetime + :param content: The resource snapshot content (in resourceChangeDetails response only). + :type content: object + """ + + _validation = { + 'timestamp': {'required': True}, + } + + _attribute_map = { + 'snapshot_id': {'key': 'snapshotId', 'type': 'str'}, + 'timestamp': {'key': 'timestamp', 'type': 'iso-8601'}, + 'content': {'key': 'content', 'type': 'object'}, + } + + def __init__( + self, + **kwargs + ): + super(ResourceChangeDataBeforeSnapshot, self).__init__(**kwargs) + + +class ResourceChangeDetailsRequestParameters(msrest.serialization.Model): + """The parameters for a specific change details request. + + All required parameters must be populated in order to send to Azure. + + :param resource_ids: Required. Specifies the list of resources for a change details request. + :type resource_ids: list[str] + :param change_ids: Required. Specifies the list of change IDs for a change details request. + :type change_ids: list[str] + """ + + _validation = { + 'resource_ids': {'required': True}, + 'change_ids': {'required': True}, + } + + _attribute_map = { + 'resource_ids': {'key': 'resourceIds', 'type': '[str]'}, + 'change_ids': {'key': 'changeIds', 'type': '[str]'}, + } + + def __init__( + self, + **kwargs + ): + super(ResourceChangeDetailsRequestParameters, self).__init__(**kwargs) + self.resource_ids = kwargs['resource_ids'] + self.change_ids = kwargs['change_ids'] + + +class ResourceChangeList(msrest.serialization.Model): + """A list of changes associated with a resource over a specific time interval. + + :param changes: The pageable value returned by the operation, i.e. a list of changes to the + resource. + + + * The list is ordered from the most recent changes to the least recent changes. + * This list will be empty if there were no changes during the requested interval. + * The ``Before`` snapshot timestamp value of the oldest change can be outside of the specified + time interval. + :type changes: list[~azure.mgmt.resourcegraph.models.ResourceChangeData] + :param skip_token: Skip token that encodes the skip information while executing the current + request. + :type skip_token: object + """ + + _attribute_map = { + 'changes': {'key': 'changes', 'type': '[ResourceChangeData]'}, + 'skip_token': {'key': '$skipToken', 'type': 'object'}, + } + + def __init__( + self, + **kwargs + ): + super(ResourceChangeList, self).__init__(**kwargs) + self.changes = kwargs.get('changes', None) + self.skip_token = kwargs.get('skip_token', None) + + +class ResourceChangesRequestParameters(msrest.serialization.Model): + """The parameters for a specific changes request. + + All required parameters must be populated in order to send to Azure. + + :param resource_ids: Specifies the list of resources for a changes request. + :type resource_ids: list[str] + :param subscription_id: The subscription id of resources to query the changes from. + :type subscription_id: str + :param interval: Required. Specifies the date and time interval for a changes request. + :type interval: ~azure.mgmt.resourcegraph.models.DateTimeInterval + :param skip_token: Acts as the continuation token for paged responses. + :type skip_token: str + :param top: The maximum number of changes the client can accept in a paged response. + :type top: int + :param table: The table name to query resources from. + :type table: str + :param fetch_property_changes: The flag if set to true will fetch property changes. + :type fetch_property_changes: bool + :param fetch_snapshots: The flag if set to true will fetch change snapshots. + :type fetch_snapshots: bool + """ + + _validation = { + 'interval': {'required': True}, + 'top': {'maximum': 1000, 'minimum': 1}, + } + + _attribute_map = { + 'resource_ids': {'key': 'resourceIds', 'type': '[str]'}, + 'subscription_id': {'key': 'subscriptionId', 'type': 'str'}, + 'interval': {'key': 'interval', 'type': 'DateTimeInterval'}, + 'skip_token': {'key': '$skipToken', 'type': 'str'}, + 'top': {'key': '$top', 'type': 'int'}, + 'table': {'key': 'table', 'type': 'str'}, + 'fetch_property_changes': {'key': 'fetchPropertyChanges', 'type': 'bool'}, + 'fetch_snapshots': {'key': 'fetchSnapshots', 'type': 'bool'}, + } + + def __init__( + self, + **kwargs + ): + super(ResourceChangesRequestParameters, self).__init__(**kwargs) + self.resource_ids = kwargs.get('resource_ids', None) + self.subscription_id = kwargs.get('subscription_id', None) + self.interval = kwargs['interval'] + self.skip_token = kwargs.get('skip_token', None) + self.top = kwargs.get('top', None) + self.table = kwargs.get('table', None) + self.fetch_property_changes = kwargs.get('fetch_property_changes', None) + self.fetch_snapshots = kwargs.get('fetch_snapshots', None) + + +class ResourceChangesRequestParametersInterval(DateTimeInterval): + """Specifies the date and time interval for a changes request. + + All required parameters must be populated in order to send to Azure. + + :param start: Required. A datetime indicating the inclusive/closed start of the time interval, + i.e. ``[``\ **\ ``start``\ **\ ``, end)``. Specifying a ``start`` that occurs chronologically + after ``end`` will result in an error. + :type start: ~datetime.datetime + :param end: Required. A datetime indicating the exclusive/open end of the time interval, i.e. + ``[start,``\ **\ ``end``\ **\ ``)``. Specifying an ``end`` that occurs chronologically before + ``start`` will result in an error. + :type end: ~datetime.datetime + """ + + _validation = { + 'start': {'required': True}, + 'end': {'required': True}, + } + + _attribute_map = { + 'start': {'key': 'start', 'type': 'iso-8601'}, + 'end': {'key': 'end', 'type': 'iso-8601'}, + } + + def __init__( + self, + **kwargs + ): + super(ResourceChangesRequestParametersInterval, self).__init__(**kwargs) + + +class ResourcePropertyChange(msrest.serialization.Model): + """The resource property change. + + All required parameters must be populated in order to send to Azure. + + :param property_name: Required. The property name. + :type property_name: str + :param before_value: The property value in before snapshot. + :type before_value: str + :param after_value: The property value in after snapshot. + :type after_value: str + :param change_category: Required. The change category. Possible values include: "User", + "System". + :type change_category: str or ~azure.mgmt.resourcegraph.models.ChangeCategory + :param property_change_type: Required. The property change Type. Possible values include: + "Insert", "Update", "Remove". + :type property_change_type: str or ~azure.mgmt.resourcegraph.models.PropertyChangeType + """ + + _validation = { + 'property_name': {'required': True}, + 'change_category': {'required': True}, + 'property_change_type': {'required': True}, + } + + _attribute_map = { + 'property_name': {'key': 'propertyName', 'type': 'str'}, + 'before_value': {'key': 'beforeValue', 'type': 'str'}, + 'after_value': {'key': 'afterValue', 'type': 'str'}, + 'change_category': {'key': 'changeCategory', 'type': 'str'}, + 'property_change_type': {'key': 'propertyChangeType', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(ResourcePropertyChange, self).__init__(**kwargs) + self.property_name = kwargs['property_name'] + self.before_value = kwargs.get('before_value', None) + self.after_value = kwargs.get('after_value', None) + self.change_category = kwargs['change_category'] + self.property_change_type = kwargs['property_change_type'] + + +class ResourcesHistoryRequest(msrest.serialization.Model): + """ResourcesHistoryRequest. + + :param subscriptions: + :type subscriptions: list[str] + :param query: + :type query: str + :param options: + :type options: ~azure.mgmt.resourcegraph.models.ResourcesHistoryRequestOptions + :param management_group_id: + :type management_group_id: str + """ + + _attribute_map = { + 'subscriptions': {'key': 'subscriptions', 'type': '[str]'}, + 'query': {'key': 'query', 'type': 'str'}, + 'options': {'key': 'options', 'type': 'ResourcesHistoryRequestOptions'}, + 'management_group_id': {'key': 'managementGroupId', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(ResourcesHistoryRequest, self).__init__(**kwargs) + self.subscriptions = kwargs.get('subscriptions', None) + self.query = kwargs.get('query', None) + self.options = kwargs.get('options', None) + self.management_group_id = kwargs.get('management_group_id', None) + + +class ResourcesHistoryRequestOptions(msrest.serialization.Model): + """ResourcesHistoryRequestOptions. + + :param interval: An interval in time specifying the date and time for the inclusive start and + exclusive end, i.e. ``[start, end)``. + :type interval: ~azure.mgmt.resourcegraph.models.DateTimeInterval + :param top: + :type top: int + :param skip: + :type skip: int + :param skip_token: + :type skip_token: str + :param result_format: Possible values include: "table", "objectArray". + :type result_format: str or + ~azure.mgmt.resourcegraph.models.ResourcesHistoryRequestOptionsResultFormat + """ + + _attribute_map = { + 'interval': {'key': 'interval', 'type': 'DateTimeInterval'}, + 'top': {'key': '$top', 'type': 'int'}, + 'skip': {'key': '$skip', 'type': 'int'}, + 'skip_token': {'key': '$skipToken', 'type': 'str'}, + 'result_format': {'key': 'resultFormat', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + super(ResourcesHistoryRequestOptions, self).__init__(**kwargs) + self.interval = kwargs.get('interval', None) + self.top = kwargs.get('top', None) + self.skip = kwargs.get('skip', None) + self.skip_token = kwargs.get('skip_token', None) + self.result_format = kwargs.get('result_format', None) + + +class Table(msrest.serialization.Model): + """Query output in tabular format. + + All required parameters must be populated in order to send to Azure. + + :param columns: Required. Query result column descriptors. + :type columns: list[~azure.mgmt.resourcegraph.models.Column] + :param rows: Required. Query result rows. + :type rows: list[list[object]] + """ + + _validation = { + 'columns': {'required': True}, + 'rows': {'required': True}, + } + + _attribute_map = { + 'columns': {'key': 'columns', 'type': '[Column]'}, + 'rows': {'key': 'rows', 'type': '[[object]]'}, + } + + def __init__( + self, + **kwargs + ): + super(Table, self).__init__(**kwargs) + self.columns = kwargs['columns'] + self.rows = kwargs['rows'] diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/models/_models_py3.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/models/_models_py3.py new file mode 100644 index 00000000000..1e01fe4052c --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/models/_models_py3.py @@ -0,0 +1,1390 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +import datetime +from typing import Dict, List, Optional, Union + +from azure.core.exceptions import HttpResponseError +import msrest.serialization + +from ._resource_graph_client_enums import * + + +class Column(msrest.serialization.Model): + """Query result column descriptor. + + All required parameters must be populated in order to send to Azure. + + :param name: Required. Column name. + :type name: str + :param type: Required. Column data type. Possible values include: "string", "integer", + "number", "boolean", "object". + :type type: str or ~azure.mgmt.resourcegraph.models.ColumnDataType + """ + + _validation = { + 'name': {'required': True}, + 'type': {'required': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + } + + def __init__( + self, + *, + name: str, + type: Union[str, "ColumnDataType"], + **kwargs + ): + super(Column, self).__init__(**kwargs) + self.name = name + self.type = type + + +class DateTimeInterval(msrest.serialization.Model): + """An interval in time specifying the date and time for the inclusive start and exclusive end, i.e. ``[start, end)``. + + All required parameters must be populated in order to send to Azure. + + :param start: Required. A datetime indicating the inclusive/closed start of the time interval. + Specifying a ``start`` that occurs chronologically + after ``end`` will result in an error. + :type start: ~datetime.datetime + :param end: Required. A datetime indicating the exclusive/open end of the time interval. + Specifying an ``end`` that occurs chronologically before + ``start`` will result in an error. + :type end: ~datetime.datetime + """ + + _validation = { + 'start': {'required': True}, + 'end': {'required': True}, + } + + _attribute_map = { + 'start': {'key': 'start', 'type': 'iso-8601'}, + 'end': {'key': 'end', 'type': 'iso-8601'}, + } + + def __init__( + self, + *, + start: datetime.datetime, + end: datetime.datetime, + **kwargs + ): + super(DateTimeInterval, self).__init__(**kwargs) + self.start = start + self.end = end + + +class Error(msrest.serialization.Model): + """Error details. + + All required parameters must be populated in order to send to Azure. + + :param code: Required. Error code identifying the specific error. + :type code: str + :param message: Required. A human readable error message. + :type message: str + :param details: Error details. + :type details: list[~azure.mgmt.resourcegraph.models.ErrorDetails] + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'details': {'key': 'details', 'type': '[ErrorDetails]'}, + } + + def __init__( + self, + *, + code: str, + message: str, + details: Optional[List["ErrorDetails"]] = None, + **kwargs + ): + super(Error, self).__init__(**kwargs) + self.code = code + self.message = message + self.details = details + + +class ErrorDetails(msrest.serialization.Model): + """Error details. + + All required parameters must be populated in order to send to Azure. + + :param additional_properties: Unmatched properties from the message are deserialized to this + collection. + :type additional_properties: dict[str, object] + :param code: Required. Error code identifying the specific error. + :type code: str + :param message: Required. A human readable error message. + :type message: str + """ + + _validation = { + 'code': {'required': True}, + 'message': {'required': True}, + } + + _attribute_map = { + 'additional_properties': {'key': '', 'type': '{object}'}, + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + } + + def __init__( + self, + *, + code: str, + message: str, + additional_properties: Optional[Dict[str, object]] = None, + **kwargs + ): + super(ErrorDetails, self).__init__(**kwargs) + self.additional_properties = additional_properties + self.code = code + self.message = message + + +class ErrorFieldContract(msrest.serialization.Model): + """Error Field contract. + + :param code: Property level error code. + :type code: str + :param message: Human-readable representation of property-level error. + :type message: str + :param target: Property name. + :type target: str + """ + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'target': {'key': 'target', 'type': 'str'}, + } + + def __init__( + self, + *, + code: Optional[str] = None, + message: Optional[str] = None, + target: Optional[str] = None, + **kwargs + ): + super(ErrorFieldContract, self).__init__(**kwargs) + self.code = code + self.message = message + self.target = target + + +class ErrorResponse(msrest.serialization.Model): + """An error response from the API. + + All required parameters must be populated in order to send to Azure. + + :param error: Required. Error information. + :type error: ~azure.mgmt.resourcegraph.models.Error + """ + + _validation = { + 'error': {'required': True}, + } + + _attribute_map = { + 'error': {'key': 'error', 'type': 'Error'}, + } + + def __init__( + self, + *, + error: "Error", + **kwargs + ): + super(ErrorResponse, self).__init__(**kwargs) + self.error = error + + +class Facet(msrest.serialization.Model): + """A facet containing additional statistics on the response of a query. Can be either FacetResult or FacetError. + + You probably want to use the sub-classes and not this class directly. Known + sub-classes are: FacetError, FacetResult. + + All required parameters must be populated in order to send to Azure. + + :param expression: Required. Facet expression, same as in the corresponding facet request. + :type expression: str + :param result_type: Required. Result type.Constant filled by server. + :type result_type: str + """ + + _validation = { + 'expression': {'required': True}, + 'result_type': {'required': True}, + } + + _attribute_map = { + 'expression': {'key': 'expression', 'type': 'str'}, + 'result_type': {'key': 'resultType', 'type': 'str'}, + } + + _subtype_map = { + 'result_type': {'FacetError': 'FacetError', 'FacetResult': 'FacetResult'} + } + + def __init__( + self, + *, + expression: str, + **kwargs + ): + super(Facet, self).__init__(**kwargs) + self.expression = expression + self.result_type = None # type: Optional[str] + + +class FacetError(Facet): + """A facet whose execution resulted in an error. + + All required parameters must be populated in order to send to Azure. + + :param expression: Required. Facet expression, same as in the corresponding facet request. + :type expression: str + :param result_type: Required. Result type.Constant filled by server. + :type result_type: str + :param errors: Required. An array containing detected facet errors with details. + :type errors: list[~azure.mgmt.resourcegraph.models.ErrorDetails] + """ + + _validation = { + 'expression': {'required': True}, + 'result_type': {'required': True}, + 'errors': {'required': True}, + } + + _attribute_map = { + 'expression': {'key': 'expression', 'type': 'str'}, + 'result_type': {'key': 'resultType', 'type': 'str'}, + 'errors': {'key': 'errors', 'type': '[ErrorDetails]'}, + } + + def __init__( + self, + *, + expression: str, + errors: List["ErrorDetails"], + **kwargs + ): + super(FacetError, self).__init__(expression=expression, **kwargs) + self.result_type = 'FacetError' # type: str + self.errors = errors + + +class FacetRequest(msrest.serialization.Model): + """A request to compute additional statistics (facets) over the query results. + + All required parameters must be populated in order to send to Azure. + + :param expression: Required. The column or list of columns to summarize by. + :type expression: str + :param options: The options for facet evaluation. + :type options: ~azure.mgmt.resourcegraph.models.FacetRequestOptions + """ + + _validation = { + 'expression': {'required': True}, + } + + _attribute_map = { + 'expression': {'key': 'expression', 'type': 'str'}, + 'options': {'key': 'options', 'type': 'FacetRequestOptions'}, + } + + def __init__( + self, + *, + expression: str, + options: Optional["FacetRequestOptions"] = None, + **kwargs + ): + super(FacetRequest, self).__init__(**kwargs) + self.expression = expression + self.options = options + + +class FacetRequestOptions(msrest.serialization.Model): + """The options for facet evaluation. + + :param sort_by: The column name or query expression to sort on. Defaults to count if not + present. + :type sort_by: str + :param sort_order: The sorting order by the selected column (count by default). Possible values + include: "asc", "desc". Default value: "desc". + :type sort_order: str or ~azure.mgmt.resourcegraph.models.FacetSortOrder + :param filter: Specifies the filter condition for the 'where' clause which will be run on main + query's result, just before the actual faceting. + :type filter: str + :param top: The maximum number of facet rows that should be returned. + :type top: int + """ + + _validation = { + 'top': {'maximum': 1000, 'minimum': 1}, + } + + _attribute_map = { + 'sort_by': {'key': 'sortBy', 'type': 'str'}, + 'sort_order': {'key': 'sortOrder', 'type': 'str'}, + 'filter': {'key': 'filter', 'type': 'str'}, + 'top': {'key': '$top', 'type': 'int'}, + } + + def __init__( + self, + *, + sort_by: Optional[str] = None, + sort_order: Optional[Union[str, "FacetSortOrder"]] = "desc", + filter: Optional[str] = None, + top: Optional[int] = None, + **kwargs + ): + super(FacetRequestOptions, self).__init__(**kwargs) + self.sort_by = sort_by + self.sort_order = sort_order + self.filter = filter + self.top = top + + +class FacetResult(Facet): + """Successfully executed facet containing additional statistics on the response of a query. + + All required parameters must be populated in order to send to Azure. + + :param expression: Required. Facet expression, same as in the corresponding facet request. + :type expression: str + :param result_type: Required. Result type.Constant filled by server. + :type result_type: str + :param total_records: Required. Number of total records in the facet results. + :type total_records: long + :param count: Required. Number of records returned in the facet response. + :type count: int + :param data: Required. A JObject array or Table containing the desired facets. Only present if + the facet is valid. + :type data: object + """ + + _validation = { + 'expression': {'required': True}, + 'result_type': {'required': True}, + 'total_records': {'required': True}, + 'count': {'required': True}, + 'data': {'required': True}, + } + + _attribute_map = { + 'expression': {'key': 'expression', 'type': 'str'}, + 'result_type': {'key': 'resultType', 'type': 'str'}, + 'total_records': {'key': 'totalRecords', 'type': 'long'}, + 'count': {'key': 'count', 'type': 'int'}, + 'data': {'key': 'data', 'type': 'object'}, + } + + def __init__( + self, + *, + expression: str, + total_records: int, + count: int, + data: object, + **kwargs + ): + super(FacetResult, self).__init__(expression=expression, **kwargs) + self.result_type = 'FacetResult' # type: str + self.total_records = total_records + self.count = count + self.data = data + + +class GraphQueryError(msrest.serialization.Model): + """Error message body that will indicate why the operation failed. + + :param code: Service-defined error code. This code serves as a sub-status for the HTTP error + code specified in the response. + :type code: str + :param message: Human-readable representation of the error. + :type message: str + :param details: The list of invalid fields send in request, in case of validation error. + :type details: list[~azure.mgmt.resourcegraph.models.ErrorFieldContract] + """ + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'details': {'key': 'details', 'type': '[ErrorFieldContract]'}, + } + + def __init__( + self, + *, + code: Optional[str] = None, + message: Optional[str] = None, + details: Optional[List["ErrorFieldContract"]] = None, + **kwargs + ): + super(GraphQueryError, self).__init__(**kwargs) + self.code = code + self.message = message + self.details = details + + +class GraphQueryListResult(msrest.serialization.Model): + """Graph query list result. + + Variables are only populated by the server, and will be ignored when sending a request. + + :param next_link: URL to fetch the next set of queries. + :type next_link: str + :ivar value: An array of graph queries. + :vartype value: list[~azure.mgmt.resourcegraph.models.GraphQueryResource] + """ + + _validation = { + 'value': {'readonly': True}, + } + + _attribute_map = { + 'next_link': {'key': 'nextLink', 'type': 'str'}, + 'value': {'key': 'value', 'type': '[GraphQueryResource]'}, + } + + def __init__( + self, + *, + next_link: Optional[str] = None, + **kwargs + ): + super(GraphQueryListResult, self).__init__(**kwargs) + self.next_link = next_link + self.value = None + + +class Resource(msrest.serialization.Model): + """An azure resource object. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar id: Azure resource Id. + :vartype id: str + :ivar name: Azure resource name. This is GUID value. The display name should be assigned within + properties field. + :vartype name: str + :param location: The location of the resource. + :type location: str + :ivar type: Azure resource type. + :vartype type: str + :param etag: This will be used to handle Optimistic Concurrency. If not present, it will always + overwrite the existing resource without checking conflict. + :type etag: str + :param tags: A set of tags. Resource tags. + :type tags: dict[str, str] + """ + + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'location': {'key': 'location', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + 'etag': {'key': 'etag', 'type': 'str'}, + 'tags': {'key': 'tags', 'type': '{str}'}, + } + + def __init__( + self, + *, + location: Optional[str] = None, + etag: Optional[str] = None, + tags: Optional[Dict[str, str]] = None, + **kwargs + ): + super(Resource, self).__init__(**kwargs) + self.id = None + self.name = None + self.location = location + self.type = None + self.etag = etag + self.tags = tags + + +class GraphQueryResource(Resource): + """Graph Query entity definition. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar id: Azure resource Id. + :vartype id: str + :ivar name: Azure resource name. This is GUID value. The display name should be assigned within + properties field. + :vartype name: str + :param location: The location of the resource. + :type location: str + :ivar type: Azure resource type. + :vartype type: str + :param etag: This will be used to handle Optimistic Concurrency. If not present, it will always + overwrite the existing resource without checking conflict. + :type etag: str + :param tags: A set of tags. Resource tags. + :type tags: dict[str, str] + :ivar time_modified: Date and time in UTC of the last modification that was made to this graph + query definition. + :vartype time_modified: ~datetime.datetime + :param description: The description of a graph query. + :type description: str + :param query: KQL query that will be graph. + :type query: str + :ivar result_kind: Enum indicating a type of graph query. Possible values include: "basic". + :vartype result_kind: str or ~azure.mgmt.resourcegraph.models.ResultKind + """ + + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, + 'time_modified': {'readonly': True}, + 'result_kind': {'readonly': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'location': {'key': 'location', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + 'etag': {'key': 'etag', 'type': 'str'}, + 'tags': {'key': 'tags', 'type': '{str}'}, + 'time_modified': {'key': 'properties.timeModified', 'type': 'iso-8601'}, + 'description': {'key': 'properties.description', 'type': 'str'}, + 'query': {'key': 'properties.query', 'type': 'str'}, + 'result_kind': {'key': 'properties.resultKind', 'type': 'str'}, + } + + def __init__( + self, + *, + location: Optional[str] = None, + etag: Optional[str] = None, + tags: Optional[Dict[str, str]] = None, + description: Optional[str] = None, + query: Optional[str] = None, + **kwargs + ): + super(GraphQueryResource, self).__init__(location=location, etag=etag, tags=tags, **kwargs) + self.time_modified = None + self.description = description + self.query = query + self.result_kind = None + + +class GraphQueryUpdateParameters(msrest.serialization.Model): + """The parameters that can be provided when updating workbook properties properties. + + :param tags: A set of tags. Resource tags. + :type tags: dict[str, str] + :param etag: This will be used to handle Optimistic Concurrency. If not present, it will always + overwrite the existing resource without checking conflict. + :type etag: str + :param description: The description of a graph query. + :type description: str + :param query: KQL query that will be graph. + :type query: str + """ + + _attribute_map = { + 'tags': {'key': 'tags', 'type': '{str}'}, + 'etag': {'key': 'etag', 'type': 'str'}, + 'description': {'key': 'properties.description', 'type': 'str'}, + 'query': {'key': 'properties.query', 'type': 'str'}, + } + + def __init__( + self, + *, + tags: Optional[Dict[str, str]] = None, + etag: Optional[str] = None, + description: Optional[str] = None, + query: Optional[str] = None, + **kwargs + ): + super(GraphQueryUpdateParameters, self).__init__(**kwargs) + self.tags = tags + self.etag = etag + self.description = description + self.query = query + + +class Operation(msrest.serialization.Model): + """Resource Graph REST API operation definition. + + :param name: Operation name: {provider}/{resource}/{operation}. + :type name: str + :param display: Display metadata associated with the operation. + :type display: ~azure.mgmt.resourcegraph.models.OperationDisplay + :param origin: The origin of operations. + :type origin: str + """ + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'display': {'key': 'display', 'type': 'OperationDisplay'}, + 'origin': {'key': 'origin', 'type': 'str'}, + } + + def __init__( + self, + *, + name: Optional[str] = None, + display: Optional["OperationDisplay"] = None, + origin: Optional[str] = None, + **kwargs + ): + super(Operation, self).__init__(**kwargs) + self.name = name + self.display = display + self.origin = origin + + +class OperationDisplay(msrest.serialization.Model): + """Display metadata associated with the operation. + + :param provider: Service provider: Microsoft Resource Graph. + :type provider: str + :param resource: Resource on which the operation is performed etc. + :type resource: str + :param operation: Type of operation: get, read, delete, etc. + :type operation: str + :param description: Description for the operation. + :type description: str + """ + + _attribute_map = { + 'provider': {'key': 'provider', 'type': 'str'}, + 'resource': {'key': 'resource', 'type': 'str'}, + 'operation': {'key': 'operation', 'type': 'str'}, + 'description': {'key': 'description', 'type': 'str'}, + } + + def __init__( + self, + *, + provider: Optional[str] = None, + resource: Optional[str] = None, + operation: Optional[str] = None, + description: Optional[str] = None, + **kwargs + ): + super(OperationDisplay, self).__init__(**kwargs) + self.provider = provider + self.resource = resource + self.operation = operation + self.description = description + + +class OperationListResult(msrest.serialization.Model): + """Result of the request to list Resource Graph operations. It contains a list of operations and a URL link to get the next set of results. + + :param value: List of Resource Graph operations supported by the Resource Graph resource + provider. + :type value: list[~azure.mgmt.resourcegraph.models.Operation] + """ + + _attribute_map = { + 'value': {'key': 'value', 'type': '[Operation]'}, + } + + def __init__( + self, + *, + value: Optional[List["Operation"]] = None, + **kwargs + ): + super(OperationListResult, self).__init__(**kwargs) + self.value = value + + +class QueryRequest(msrest.serialization.Model): + """Describes a query to be executed. + + All required parameters must be populated in order to send to Azure. + + :param subscriptions: Azure subscriptions against which to execute the query. + :type subscriptions: list[str] + :param management_groups: Azure management groups against which to execute the query. Example: + [ 'mg1', 'mg2' ]. + :type management_groups: list[str] + :param query: Required. The resources query. + :type query: str + :param options: The query evaluation options. + :type options: ~azure.mgmt.resourcegraph.models.QueryRequestOptions + :param facets: An array of facet requests to be computed against the query result. + :type facets: list[~azure.mgmt.resourcegraph.models.FacetRequest] + """ + + _validation = { + 'query': {'required': True}, + } + + _attribute_map = { + 'subscriptions': {'key': 'subscriptions', 'type': '[str]'}, + 'management_groups': {'key': 'managementGroups', 'type': '[str]'}, + 'query': {'key': 'query', 'type': 'str'}, + 'options': {'key': 'options', 'type': 'QueryRequestOptions'}, + 'facets': {'key': 'facets', 'type': '[FacetRequest]'}, + } + + def __init__( + self, + *, + query: str, + subscriptions: Optional[List[str]] = None, + management_groups: Optional[List[str]] = None, + options: Optional["QueryRequestOptions"] = None, + facets: Optional[List["FacetRequest"]] = None, + **kwargs + ): + super(QueryRequest, self).__init__(**kwargs) + self.subscriptions = subscriptions + self.management_groups = management_groups + self.query = query + self.options = options + self.facets = facets + + +class QueryRequestOptions(msrest.serialization.Model): + """The options for query evaluation. + + :param skip_token: Continuation token for pagination, capturing the next page size and offset, + as well as the context of the query. + :type skip_token: str + :param top: The maximum number of rows that the query should return. Overrides the page size + when ``$skipToken`` property is present. + :type top: int + :param skip: The number of rows to skip from the beginning of the results. Overrides the next + page offset when ``$skipToken`` property is present. + :type skip: int + :param result_format: Defines in which format query result returned. Possible values include: + "table", "objectArray". Default value: "objectArray". + :type result_format: str or ~azure.mgmt.resourcegraph.models.ResultFormat + :param allow_partial_scopes: Only applicable for tenant and management group level queries to + decide whether to allow partial scopes for result in case the number of subscriptions exceed + allowed limits. + :type allow_partial_scopes: bool + """ + + _validation = { + 'top': {'maximum': 1000, 'minimum': 1}, + 'skip': {'minimum': 0}, + } + + _attribute_map = { + 'skip_token': {'key': '$skipToken', 'type': 'str'}, + 'top': {'key': '$top', 'type': 'int'}, + 'skip': {'key': '$skip', 'type': 'int'}, + 'result_format': {'key': 'resultFormat', 'type': 'str'}, + 'allow_partial_scopes': {'key': 'allowPartialScopes', 'type': 'bool'}, + } + + def __init__( + self, + *, + skip_token: Optional[str] = None, + top: Optional[int] = None, + skip: Optional[int] = None, + result_format: Optional[Union[str, "ResultFormat"]] = "objectArray", + allow_partial_scopes: Optional[bool] = False, + **kwargs + ): + super(QueryRequestOptions, self).__init__(**kwargs) + self.skip_token = skip_token + self.top = top + self.skip = skip + self.result_format = result_format + self.allow_partial_scopes = allow_partial_scopes + + +class QueryResponse(msrest.serialization.Model): + """Query result. + + All required parameters must be populated in order to send to Azure. + + :param total_records: Required. Number of total records matching the query. + :type total_records: long + :param count: Required. Number of records returned in the current response. In the case of + paging, this is the number of records in the current page. + :type count: long + :param result_truncated: Required. Indicates whether the query results are truncated. Possible + values include: "true", "false". + :type result_truncated: str or ~azure.mgmt.resourcegraph.models.ResultTruncated + :param skip_token: When present, the value can be passed to a subsequent query call (together + with the same query and scopes used in the current request) to retrieve the next page of data. + :type skip_token: str + :param data: Required. Query output in JObject array or Table format. + :type data: object + :param facets: Query facets. + :type facets: list[~azure.mgmt.resourcegraph.models.Facet] + """ + + _validation = { + 'total_records': {'required': True}, + 'count': {'required': True}, + 'result_truncated': {'required': True}, + 'data': {'required': True}, + } + + _attribute_map = { + 'total_records': {'key': 'totalRecords', 'type': 'long'}, + 'count': {'key': 'count', 'type': 'long'}, + 'result_truncated': {'key': 'resultTruncated', 'type': 'str'}, + 'skip_token': {'key': '$skipToken', 'type': 'str'}, + 'data': {'key': 'data', 'type': 'object'}, + 'facets': {'key': 'facets', 'type': '[Facet]'}, + } + + def __init__( + self, + *, + total_records: int, + count: int, + result_truncated: Union[str, "ResultTruncated"], + data: object, + skip_token: Optional[str] = None, + facets: Optional[List["Facet"]] = None, + **kwargs + ): + super(QueryResponse, self).__init__(**kwargs) + self.total_records = total_records + self.count = count + self.result_truncated = result_truncated + self.skip_token = skip_token + self.data = data + self.facets = facets + + +class ResourceChangeData(msrest.serialization.Model): + """Data on a specific change, represented by a pair of before and after resource snapshots. + + All required parameters must be populated in order to send to Azure. + + :param resource_id: The resource for a change. + :type resource_id: str + :param change_id: Required. The change ID. Valid and unique within the specified resource only. + :type change_id: str + :param before_snapshot: Required. The snapshot before the change. + :type before_snapshot: ~azure.mgmt.resourcegraph.models.ResourceSnapshotData + :param after_snapshot: Required. The snapshot after the change. + :type after_snapshot: ~azure.mgmt.resourcegraph.models.ResourceSnapshotData + :param change_type: The change type for snapshot. PropertyChanges will be provided in case of + Update change type. Possible values include: "Create", "Update", "Delete". + :type change_type: str or ~azure.mgmt.resourcegraph.models.ChangeType + :param property_changes: An array of resource property change. + :type property_changes: list[~azure.mgmt.resourcegraph.models.ResourcePropertyChange] + """ + + _validation = { + 'change_id': {'required': True}, + 'before_snapshot': {'required': True}, + 'after_snapshot': {'required': True}, + } + + _attribute_map = { + 'resource_id': {'key': 'resourceId', 'type': 'str'}, + 'change_id': {'key': 'changeId', 'type': 'str'}, + 'before_snapshot': {'key': 'beforeSnapshot', 'type': 'ResourceSnapshotData'}, + 'after_snapshot': {'key': 'afterSnapshot', 'type': 'ResourceSnapshotData'}, + 'change_type': {'key': 'changeType', 'type': 'str'}, + 'property_changes': {'key': 'propertyChanges', 'type': '[ResourcePropertyChange]'}, + } + + def __init__( + self, + *, + change_id: str, + before_snapshot: "ResourceSnapshotData", + after_snapshot: "ResourceSnapshotData", + resource_id: Optional[str] = None, + change_type: Optional[Union[str, "ChangeType"]] = None, + property_changes: Optional[List["ResourcePropertyChange"]] = None, + **kwargs + ): + super(ResourceChangeData, self).__init__(**kwargs) + self.resource_id = resource_id + self.change_id = change_id + self.before_snapshot = before_snapshot + self.after_snapshot = after_snapshot + self.change_type = change_type + self.property_changes = property_changes + + +class ResourceSnapshotData(msrest.serialization.Model): + """Data on a specific resource snapshot. + + All required parameters must be populated in order to send to Azure. + + :param snapshot_id: The ID of the snapshot. + :type snapshot_id: str + :param timestamp: Required. The time when the snapshot was created. + The snapshot timestamp provides an approximation as to when a modification to a resource was + detected. There can be a difference between the actual modification time and the detection + time. This is due to differences in how operations that modify a resource are processed, + versus how operation that record resource snapshots are processed. + :type timestamp: ~datetime.datetime + :param content: The resource snapshot content (in resourceChangeDetails response only). + :type content: object + """ + + _validation = { + 'timestamp': {'required': True}, + } + + _attribute_map = { + 'snapshot_id': {'key': 'snapshotId', 'type': 'str'}, + 'timestamp': {'key': 'timestamp', 'type': 'iso-8601'}, + 'content': {'key': 'content', 'type': 'object'}, + } + + def __init__( + self, + *, + timestamp: datetime.datetime, + snapshot_id: Optional[str] = None, + content: Optional[object] = None, + **kwargs + ): + super(ResourceSnapshotData, self).__init__(**kwargs) + self.snapshot_id = snapshot_id + self.timestamp = timestamp + self.content = content + + +class ResourceChangeDataAfterSnapshot(ResourceSnapshotData): + """The snapshot after the change. + + All required parameters must be populated in order to send to Azure. + + :param snapshot_id: The ID of the snapshot. + :type snapshot_id: str + :param timestamp: Required. The time when the snapshot was created. + The snapshot timestamp provides an approximation as to when a modification to a resource was + detected. There can be a difference between the actual modification time and the detection + time. This is due to differences in how operations that modify a resource are processed, + versus how operation that record resource snapshots are processed. + :type timestamp: ~datetime.datetime + :param content: The resource snapshot content (in resourceChangeDetails response only). + :type content: object + """ + + _validation = { + 'timestamp': {'required': True}, + } + + _attribute_map = { + 'snapshot_id': {'key': 'snapshotId', 'type': 'str'}, + 'timestamp': {'key': 'timestamp', 'type': 'iso-8601'}, + 'content': {'key': 'content', 'type': 'object'}, + } + + def __init__( + self, + *, + timestamp: datetime.datetime, + snapshot_id: Optional[str] = None, + content: Optional[object] = None, + **kwargs + ): + super(ResourceChangeDataAfterSnapshot, self).__init__(snapshot_id=snapshot_id, timestamp=timestamp, content=content, **kwargs) + + +class ResourceChangeDataBeforeSnapshot(ResourceSnapshotData): + """The snapshot before the change. + + All required parameters must be populated in order to send to Azure. + + :param snapshot_id: The ID of the snapshot. + :type snapshot_id: str + :param timestamp: Required. The time when the snapshot was created. + The snapshot timestamp provides an approximation as to when a modification to a resource was + detected. There can be a difference between the actual modification time and the detection + time. This is due to differences in how operations that modify a resource are processed, + versus how operation that record resource snapshots are processed. + :type timestamp: ~datetime.datetime + :param content: The resource snapshot content (in resourceChangeDetails response only). + :type content: object + """ + + _validation = { + 'timestamp': {'required': True}, + } + + _attribute_map = { + 'snapshot_id': {'key': 'snapshotId', 'type': 'str'}, + 'timestamp': {'key': 'timestamp', 'type': 'iso-8601'}, + 'content': {'key': 'content', 'type': 'object'}, + } + + def __init__( + self, + *, + timestamp: datetime.datetime, + snapshot_id: Optional[str] = None, + content: Optional[object] = None, + **kwargs + ): + super(ResourceChangeDataBeforeSnapshot, self).__init__(snapshot_id=snapshot_id, timestamp=timestamp, content=content, **kwargs) + + +class ResourceChangeDetailsRequestParameters(msrest.serialization.Model): + """The parameters for a specific change details request. + + All required parameters must be populated in order to send to Azure. + + :param resource_ids: Required. Specifies the list of resources for a change details request. + :type resource_ids: list[str] + :param change_ids: Required. Specifies the list of change IDs for a change details request. + :type change_ids: list[str] + """ + + _validation = { + 'resource_ids': {'required': True}, + 'change_ids': {'required': True}, + } + + _attribute_map = { + 'resource_ids': {'key': 'resourceIds', 'type': '[str]'}, + 'change_ids': {'key': 'changeIds', 'type': '[str]'}, + } + + def __init__( + self, + *, + resource_ids: List[str], + change_ids: List[str], + **kwargs + ): + super(ResourceChangeDetailsRequestParameters, self).__init__(**kwargs) + self.resource_ids = resource_ids + self.change_ids = change_ids + + +class ResourceChangeList(msrest.serialization.Model): + """A list of changes associated with a resource over a specific time interval. + + :param changes: The pageable value returned by the operation, i.e. a list of changes to the + resource. + + + * The list is ordered from the most recent changes to the least recent changes. + * This list will be empty if there were no changes during the requested interval. + * The ``Before`` snapshot timestamp value of the oldest change can be outside of the specified + time interval. + :type changes: list[~azure.mgmt.resourcegraph.models.ResourceChangeData] + :param skip_token: Skip token that encodes the skip information while executing the current + request. + :type skip_token: object + """ + + _attribute_map = { + 'changes': {'key': 'changes', 'type': '[ResourceChangeData]'}, + 'skip_token': {'key': '$skipToken', 'type': 'object'}, + } + + def __init__( + self, + *, + changes: Optional[List["ResourceChangeData"]] = None, + skip_token: Optional[object] = None, + **kwargs + ): + super(ResourceChangeList, self).__init__(**kwargs) + self.changes = changes + self.skip_token = skip_token + + +class ResourceChangesRequestParameters(msrest.serialization.Model): + """The parameters for a specific changes request. + + All required parameters must be populated in order to send to Azure. + + :param resource_ids: Specifies the list of resources for a changes request. + :type resource_ids: list[str] + :param subscription_id: The subscription id of resources to query the changes from. + :type subscription_id: str + :param interval: Required. Specifies the date and time interval for a changes request. + :type interval: ~azure.mgmt.resourcegraph.models.DateTimeInterval + :param skip_token: Acts as the continuation token for paged responses. + :type skip_token: str + :param top: The maximum number of changes the client can accept in a paged response. + :type top: int + :param table: The table name to query resources from. + :type table: str + :param fetch_property_changes: The flag if set to true will fetch property changes. + :type fetch_property_changes: bool + :param fetch_snapshots: The flag if set to true will fetch change snapshots. + :type fetch_snapshots: bool + """ + + _validation = { + 'interval': {'required': True}, + 'top': {'maximum': 1000, 'minimum': 1}, + } + + _attribute_map = { + 'resource_ids': {'key': 'resourceIds', 'type': '[str]'}, + 'subscription_id': {'key': 'subscriptionId', 'type': 'str'}, + 'interval': {'key': 'interval', 'type': 'DateTimeInterval'}, + 'skip_token': {'key': '$skipToken', 'type': 'str'}, + 'top': {'key': '$top', 'type': 'int'}, + 'table': {'key': 'table', 'type': 'str'}, + 'fetch_property_changes': {'key': 'fetchPropertyChanges', 'type': 'bool'}, + 'fetch_snapshots': {'key': 'fetchSnapshots', 'type': 'bool'}, + } + + def __init__( + self, + *, + interval: "DateTimeInterval", + resource_ids: Optional[List[str]] = None, + subscription_id: Optional[str] = None, + skip_token: Optional[str] = None, + top: Optional[int] = None, + table: Optional[str] = None, + fetch_property_changes: Optional[bool] = None, + fetch_snapshots: Optional[bool] = None, + **kwargs + ): + super(ResourceChangesRequestParameters, self).__init__(**kwargs) + self.resource_ids = resource_ids + self.subscription_id = subscription_id + self.interval = interval + self.skip_token = skip_token + self.top = top + self.table = table + self.fetch_property_changes = fetch_property_changes + self.fetch_snapshots = fetch_snapshots + + +class ResourceChangesRequestParametersInterval(DateTimeInterval): + """Specifies the date and time interval for a changes request. + + All required parameters must be populated in order to send to Azure. + + :param start: Required. A datetime indicating the inclusive/closed start of the time interval. + Specifying a ``start`` that occurs chronologically + after ``end`` will result in an error. + :type start: ~datetime.datetime + :param end: Required. A datetime indicating the exclusive/open end of the time interval. + Specifying an ``end`` that occurs chronologically before + ``start`` will result in an error. + :type end: ~datetime.datetime + """ + + _validation = { + 'start': {'required': True}, + 'end': {'required': True}, + } + + _attribute_map = { + 'start': {'key': 'start', 'type': 'iso-8601'}, + 'end': {'key': 'end', 'type': 'iso-8601'}, + } + + def __init__( + self, + *, + start: datetime.datetime, + end: datetime.datetime, + **kwargs + ): + super(ResourceChangesRequestParametersInterval, self).__init__(start=start, end=end, **kwargs) + + +class ResourcePropertyChange(msrest.serialization.Model): + """The resource property change. + + All required parameters must be populated in order to send to Azure. + + :param property_name: Required. The property name. + :type property_name: str + :param before_value: The property value in before snapshot. + :type before_value: str + :param after_value: The property value in after snapshot. + :type after_value: str + :param change_category: Required. The change category. Possible values include: "User", + "System". + :type change_category: str or ~azure.mgmt.resourcegraph.models.ChangeCategory + :param property_change_type: Required. The property change Type. Possible values include: + "Insert", "Update", "Remove". + :type property_change_type: str or ~azure.mgmt.resourcegraph.models.PropertyChangeType + """ + + _validation = { + 'property_name': {'required': True}, + 'change_category': {'required': True}, + 'property_change_type': {'required': True}, + } + + _attribute_map = { + 'property_name': {'key': 'propertyName', 'type': 'str'}, + 'before_value': {'key': 'beforeValue', 'type': 'str'}, + 'after_value': {'key': 'afterValue', 'type': 'str'}, + 'change_category': {'key': 'changeCategory', 'type': 'str'}, + 'property_change_type': {'key': 'propertyChangeType', 'type': 'str'}, + } + + def __init__( + self, + *, + property_name: str, + change_category: Union[str, "ChangeCategory"], + property_change_type: Union[str, "PropertyChangeType"], + before_value: Optional[str] = None, + after_value: Optional[str] = None, + **kwargs + ): + super(ResourcePropertyChange, self).__init__(**kwargs) + self.property_name = property_name + self.before_value = before_value + self.after_value = after_value + self.change_category = change_category + self.property_change_type = property_change_type + + +class ResourcesHistoryRequest(msrest.serialization.Model): + """ResourcesHistoryRequest. + + :param subscriptions: + :type subscriptions: list[str] + :param query: + :type query: str + :param options: + :type options: ~azure.mgmt.resourcegraph.models.ResourcesHistoryRequestOptions + :param management_group_id: + :type management_group_id: str + """ + + _attribute_map = { + 'subscriptions': {'key': 'subscriptions', 'type': '[str]'}, + 'query': {'key': 'query', 'type': 'str'}, + 'options': {'key': 'options', 'type': 'ResourcesHistoryRequestOptions'}, + 'management_group_id': {'key': 'managementGroupId', 'type': 'str'}, + } + + def __init__( + self, + *, + subscriptions: Optional[List[str]] = None, + query: Optional[str] = None, + options: Optional["ResourcesHistoryRequestOptions"] = None, + management_group_id: Optional[str] = None, + **kwargs + ): + super(ResourcesHistoryRequest, self).__init__(**kwargs) + self.subscriptions = subscriptions + self.query = query + self.options = options + self.management_group_id = management_group_id + + +class ResourcesHistoryRequestOptions(msrest.serialization.Model): + """ResourcesHistoryRequestOptions. + + :param interval: An interval in time specifying the date and time for the inclusive start and + exclusive end, i.e. ``[start, end)``. + :type interval: ~azure.mgmt.resourcegraph.models.DateTimeInterval + :param top: + :type top: int + :param skip: + :type skip: int + :param skip_token: + :type skip_token: str + :param result_format: Possible values include: "table", "objectArray". + :type result_format: str or + ~azure.mgmt.resourcegraph.models.ResourcesHistoryRequestOptionsResultFormat + """ + + _attribute_map = { + 'interval': {'key': 'interval', 'type': 'DateTimeInterval'}, + 'top': {'key': '$top', 'type': 'int'}, + 'skip': {'key': '$skip', 'type': 'int'}, + 'skip_token': {'key': '$skipToken', 'type': 'str'}, + 'result_format': {'key': 'resultFormat', 'type': 'str'}, + } + + def __init__( + self, + *, + interval: Optional["DateTimeInterval"] = None, + top: Optional[int] = None, + skip: Optional[int] = None, + skip_token: Optional[str] = None, + result_format: Optional[Union[str, "ResourcesHistoryRequestOptionsResultFormat"]] = None, + **kwargs + ): + super(ResourcesHistoryRequestOptions, self).__init__(**kwargs) + self.interval = interval + self.top = top + self.skip = skip + self.skip_token = skip_token + self.result_format = result_format + + +class Table(msrest.serialization.Model): + """Query output in tabular format. + + All required parameters must be populated in order to send to Azure. + + :param columns: Required. Query result column descriptors. + :type columns: list[~azure.mgmt.resourcegraph.models.Column] + :param rows: Required. Query result rows. + :type rows: list[list[object]] + """ + + _validation = { + 'columns': {'required': True}, + 'rows': {'required': True}, + } + + _attribute_map = { + 'columns': {'key': 'columns', 'type': '[Column]'}, + 'rows': {'key': 'rows', 'type': '[[object]]'}, + } + + def __init__( + self, + *, + columns: List["Column"], + rows: List[List[object]], + **kwargs + ): + super(Table, self).__init__(**kwargs) + self.columns = columns + self.rows = rows diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/models/_resource_graph_client_enums.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/models/_resource_graph_client_enums.py new file mode 100644 index 00000000000..917bea73469 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/models/_resource_graph_client_enums.py @@ -0,0 +1,92 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from enum import Enum, EnumMeta +from six import with_metaclass + +class _CaseInsensitiveEnumMeta(EnumMeta): + def __getitem__(self, name): + return super().__getitem__(name.upper()) + + def __getattr__(cls, name): + """Return the enum member matching `name` + We use __getattr__ instead of descriptors or inserting into the enum + class' __dict__ in order to support `name` and `value` being both + properties for enum members (which live in the class' __dict__) and + enum members themselves. + """ + try: + return cls._member_map_[name.upper()] + except KeyError: + raise AttributeError(name) + + +class ChangeCategory(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """The change category. + """ + + USER = "User" + SYSTEM = "System" + +class ChangeType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """The change type for snapshot. PropertyChanges will be provided in case of Update change type + """ + + CREATE = "Create" + UPDATE = "Update" + DELETE = "Delete" + +class ColumnDataType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """Data type of a column in a table. + """ + + STRING = "string" + INTEGER = "integer" + NUMBER = "number" + BOOLEAN = "boolean" + OBJECT = "object" + +class FacetSortOrder(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """The sorting order by the selected column (count by default). + """ + + ASC = "asc" + DESC = "desc" + +class PropertyChangeType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """The property change Type + """ + + INSERT = "Insert" + UPDATE = "Update" + REMOVE = "Remove" + +class ResourcesHistoryRequestOptionsResultFormat(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + + TABLE = "table" + OBJECT_ARRAY = "objectArray" + +class ResultFormat(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """Defines in which format query result returned. + """ + + TABLE = "table" + OBJECT_ARRAY = "objectArray" + +class ResultKind(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """Enum indicating a type of graph query. + """ + + BASIC = "basic" + +class ResultTruncated(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)): + """Indicates whether the query results are truncated. + """ + + TRUE = "true" + FALSE = "false" diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/__init__.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/__init__.py new file mode 100644 index 00000000000..21a67ce4dae --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/__init__.py @@ -0,0 +1,17 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from ._resource_graph_client_operations import ResourceGraphClientOperationsMixin +from ._operations import Operations +from ._graph_query_operations import GraphQueryOperations + +__all__ = [ + 'ResourceGraphClientOperationsMixin', + 'Operations', + 'GraphQueryOperations', +] diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/_graph_query_operations.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/_graph_query_operations.py new file mode 100644 index 00000000000..59f6ed9dc07 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/_graph_query_operations.py @@ -0,0 +1,371 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING +import warnings + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.paging import ItemPaged +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpRequest, HttpResponse +from azure.mgmt.core.exceptions import ARMErrorFormat + +from .. import models as _models + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Callable, Dict, Generic, Iterable, Optional, TypeVar + + T = TypeVar('T') + ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +class GraphQueryOperations(object): + """GraphQueryOperations operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~azure.mgmt.resourcegraph.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = _models + + def __init__(self, client, config, serializer, deserializer): + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + def list( + self, + resource_group_name, # type: str + **kwargs # type: Any + ): + # type: (...) -> Iterable["_models.GraphQueryListResult"] + """Get all graph queries defined within a specified subscription and resource group. + + :param resource_group_name: The name of the resource group. + :type resource_group_name: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: An iterator like instance of either GraphQueryListResult or the result of cls(response) + :rtype: ~azure.core.paging.ItemPaged[~azure.mgmt.resourcegraph.models.GraphQueryListResult] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GraphQueryListResult"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2018-09-01-preview" + accept = "application/json" + + def prepare_request(next_link=None): + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + if not next_link: + # Construct URL + url = self.list.metadata['url'] # type: ignore + path_format_arguments = { + 'subscriptionId': self._serialize.url("self._config.subscription_id", self._config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + else: + url = next_link + query_parameters = {} # type: Dict[str, Any] + request = self._client.get(url, query_parameters, header_parameters) + return request + + def extract_data(pipeline_response): + deserialized = self._deserialize('GraphQueryListResult', pipeline_response) + list_of_elem = deserialized.value + if cls: + list_of_elem = cls(list_of_elem) + return deserialized.next_link or None, iter(list_of_elem) + + def get_next(next_link=None): + request = prepare_request(next_link) + + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + error = self._deserialize(_models.GraphQueryError, response) + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + return pipeline_response + + return ItemPaged( + get_next, extract_data + ) + list.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ResourceGraph/queries'} # type: ignore + + def get( + self, + resource_group_name, # type: str + resource_name, # type: str + **kwargs # type: Any + ): + # type: (...) -> "_models.GraphQueryResource" + """Get a single graph query by its resourceName. + + :param resource_group_name: The name of the resource group. + :type resource_group_name: str + :param resource_name: The name of the Graph Query resource. + :type resource_name: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: GraphQueryResource, or the result of cls(response) + :rtype: ~azure.mgmt.resourcegraph.models.GraphQueryResource + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GraphQueryResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2018-09-01-preview" + accept = "application/json" + + # Construct URL + url = self.get.metadata['url'] # type: ignore + path_format_arguments = { + 'subscriptionId': self._serialize.url("self._config.subscription_id", self._config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.GraphQueryError, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('GraphQueryResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + get.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ResourceGraph/queries/{resourceName}'} # type: ignore + + def delete( + self, + resource_group_name, # type: str + resource_name, # type: str + **kwargs # type: Any + ): + # type: (...) -> None + """Delete a graph query. + + :param resource_group_name: The name of the resource group. + :type resource_group_name: str + :param resource_name: The name of the Graph Query resource. + :type resource_name: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: None, or the result of cls(response) + :rtype: None + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2018-09-01-preview" + accept = "application/json" + + # Construct URL + url = self.delete.metadata['url'] # type: ignore + path_format_arguments = { + 'subscriptionId': self._serialize.url("self._config.subscription_id", self._config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + request = self._client.delete(url, query_parameters, header_parameters) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200, 204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.GraphQueryError, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + if cls: + return cls(pipeline_response, None, {}) + + delete.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ResourceGraph/queries/{resourceName}'} # type: ignore + + def create_or_update( + self, + resource_group_name, # type: str + resource_name, # type: str + properties, # type: "_models.GraphQueryResource" + **kwargs # type: Any + ): + # type: (...) -> "_models.GraphQueryResource" + """Create a new graph query. + + :param resource_group_name: The name of the resource group. + :type resource_group_name: str + :param resource_name: The name of the Graph Query resource. + :type resource_name: str + :param properties: Properties that need to be specified to create a new graph query. + :type properties: ~azure.mgmt.resourcegraph.models.GraphQueryResource + :keyword callable cls: A custom type or function that will be passed the direct response + :return: GraphQueryResource, or the result of cls(response) + :rtype: ~azure.mgmt.resourcegraph.models.GraphQueryResource + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GraphQueryResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2018-09-01-preview" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.create_or_update.metadata['url'] # type: ignore + path_format_arguments = { + 'subscriptionId': self._serialize.url("self._config.subscription_id", self._config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(properties, 'GraphQueryResource') + body_content_kwargs['content'] = body_content + request = self._client.put(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.GraphQueryError, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('GraphQueryResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + create_or_update.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ResourceGraph/queries/{resourceName}'} # type: ignore + + def update( + self, + resource_group_name, # type: str + resource_name, # type: str + body, # type: "_models.GraphQueryUpdateParameters" + **kwargs # type: Any + ): + # type: (...) -> "_models.GraphQueryResource" + """Updates a graph query that has already been added. + + :param resource_group_name: The name of the resource group. + :type resource_group_name: str + :param resource_name: The name of the Graph Query resource. + :type resource_name: str + :param body: Properties that need to be specified to create a new graph query. + :type body: ~azure.mgmt.resourcegraph.models.GraphQueryUpdateParameters + :keyword callable cls: A custom type or function that will be passed the direct response + :return: GraphQueryResource, or the result of cls(response) + :rtype: ~azure.mgmt.resourcegraph.models.GraphQueryResource + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GraphQueryResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2018-09-01-preview" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.update.metadata['url'] # type: ignore + path_format_arguments = { + 'subscriptionId': self._serialize.url("self._config.subscription_id", self._config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str'), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str'), + } + url = self._client.format_url(url, **path_format_arguments) + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(body, 'GraphQueryUpdateParameters') + body_content_kwargs['content'] = body_content + request = self._client.patch(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.GraphQueryError, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('GraphQueryResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + update.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ResourceGraph/queries/{resourceName}'} # type: ignore diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/_operations.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/_operations.py new file mode 100644 index 00000000000..baeed524e44 --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/_operations.py @@ -0,0 +1,110 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING +import warnings + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.paging import ItemPaged +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpRequest, HttpResponse +from azure.mgmt.core.exceptions import ARMErrorFormat + +from .. import models as _models + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Callable, Dict, Generic, Iterable, Optional, TypeVar + + T = TypeVar('T') + ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +class Operations(object): + """Operations operations. + + You should not instantiate this class directly. Instead, you should create a Client instance that + instantiates it for you and attaches it as an attribute. + + :ivar models: Alias to model classes used in this operation group. + :type models: ~azure.mgmt.resourcegraph.models + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + """ + + models = _models + + def __init__(self, client, config, serializer, deserializer): + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self._config = config + + def list( + self, + **kwargs # type: Any + ): + # type: (...) -> Iterable["_models.OperationListResult"] + """Lists all of the available REST API operations. + + :keyword callable cls: A custom type or function that will be passed the direct response + :return: An iterator like instance of either OperationListResult or the result of cls(response) + :rtype: ~azure.core.paging.ItemPaged[~azure.mgmt.resourcegraph.models.OperationListResult] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.OperationListResult"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-01" + accept = "application/json" + + def prepare_request(next_link=None): + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + if not next_link: + # Construct URL + url = self.list.metadata['url'] # type: ignore + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + request = self._client.get(url, query_parameters, header_parameters) + else: + url = next_link + query_parameters = {} # type: Dict[str, Any] + request = self._client.get(url, query_parameters, header_parameters) + return request + + def extract_data(pipeline_response): + deserialized = self._deserialize('OperationListResult', pipeline_response) + list_of_elem = deserialized.value + if cls: + list_of_elem = cls(list_of_elem) + return None, iter(list_of_elem) + + def get_next(next_link=None): + request = prepare_request(next_link) + + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + error = self._deserialize(_models.ErrorResponse, response) + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + return pipeline_response + + return ItemPaged( + get_next, extract_data + ) + list.metadata = {'url': '/providers/Microsoft.ResourceGraph/operations'} # type: ignore diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/_resource_graph_client_operations.py b/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/_resource_graph_client_operations.py new file mode 100644 index 00000000000..71ada398b4d --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/operations/_resource_graph_client_operations.py @@ -0,0 +1,249 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) AutoRest Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from typing import TYPE_CHECKING +import warnings + +from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ResourceExistsError, ResourceNotFoundError, map_error +from azure.core.pipeline import PipelineResponse +from azure.core.pipeline.transport import HttpRequest, HttpResponse +from azure.mgmt.core.exceptions import ARMErrorFormat + +from .. import models as _models + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from typing import Any, Callable, Dict, Generic, List, Optional, TypeVar + + T = TypeVar('T') + ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +class ResourceGraphClientOperationsMixin(object): + + def resource_changes( + self, + parameters, # type: "_models.ResourceChangesRequestParameters" + **kwargs # type: Any + ): + # type: (...) -> "_models.ResourceChangeList" + """List changes to a resource for a given time interval. + + :param parameters: the parameters for this request for changes. + :type parameters: ~azure.mgmt.resourcegraph.models.ResourceChangesRequestParameters + :keyword callable cls: A custom type or function that will be passed the direct response + :return: ResourceChangeList, or the result of cls(response) + :rtype: ~azure.mgmt.resourcegraph.models.ResourceChangeList + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.ResourceChangeList"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2020-09-01-preview" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.resource_changes.metadata['url'] # type: ignore + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(parameters, 'ResourceChangesRequestParameters') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.ErrorResponse, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('ResourceChangeList', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + resource_changes.metadata = {'url': '/providers/Microsoft.ResourceGraph/resourceChanges'} # type: ignore + + def resource_change_details( + self, + parameters, # type: "_models.ResourceChangeDetailsRequestParameters" + **kwargs # type: Any + ): + # type: (...) -> List["_models.ResourceChangeData"] + """Get resource change details. + + :param parameters: The parameters for this request for resource change details. + :type parameters: ~azure.mgmt.resourcegraph.models.ResourceChangeDetailsRequestParameters + :keyword callable cls: A custom type or function that will be passed the direct response + :return: list of ResourceChangeData, or the result of cls(response) + :rtype: list[~azure.mgmt.resourcegraph.models.ResourceChangeData] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[List["_models.ResourceChangeData"]] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2020-09-01-preview" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.resource_change_details.metadata['url'] # type: ignore + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(parameters, 'ResourceChangeDetailsRequestParameters') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.ErrorResponse, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('[ResourceChangeData]', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + resource_change_details.metadata = {'url': '/providers/Microsoft.ResourceGraph/resourceChangeDetails'} # type: ignore + + def resources( + self, + query, # type: "_models.QueryRequest" + **kwargs # type: Any + ): + # type: (...) -> "_models.QueryResponse" + """Queries the resources managed by Azure Resource Manager for scopes specified in the request. + + :param query: Request specifying query and its options. + :type query: ~azure.mgmt.resourcegraph.models.QueryRequest + :keyword callable cls: A custom type or function that will be passed the direct response + :return: QueryResponse, or the result of cls(response) + :rtype: ~azure.mgmt.resourcegraph.models.QueryResponse + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.QueryResponse"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2021-03-01" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.resources.metadata['url'] # type: ignore + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(query, 'QueryRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.ErrorResponse, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('QueryResponse', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + resources.metadata = {'url': '/providers/Microsoft.ResourceGraph/resources'} # type: ignore + + def resources_history( + self, + request, # type: "_models.ResourcesHistoryRequest" + **kwargs # type: Any + ): + # type: (...) -> object + """List all snapshots of a resource for a given time interval. + + :param request: + :type request: ~azure.mgmt.resourcegraph.models.ResourcesHistoryRequest + :keyword callable cls: A custom type or function that will be passed the direct response + :return: object, or the result of cls(response) + :rtype: object + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType[object] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + api_version = "2020-04-01-preview" + content_type = kwargs.pop("content_type", "application/json") + accept = "application/json" + + # Construct URL + url = self.resources_history.metadata['url'] # type: ignore + + # Construct parameters + query_parameters = {} # type: Dict[str, Any] + query_parameters['api-version'] = self._serialize.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = {} # type: Dict[str, Any] + header_parameters['Content-Type'] = self._serialize.header("content_type", content_type, 'str') + header_parameters['Accept'] = self._serialize.header("accept", accept, 'str') + + body_content_kwargs = {} # type: Dict[str, Any] + body_content = self._serialize.body(request, 'ResourcesHistoryRequest') + body_content_kwargs['content'] = body_content + request = self._client.post(url, query_parameters, header_parameters, **body_content_kwargs) + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize(_models.ErrorResponse, response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('object', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + resources_history.metadata = {'url': '/providers/Microsoft.ResourceGraph/resourcesHistory'} # type: ignore diff --git a/src/zones/azext_zones/vendored_sdks/resourcegraph/py.typed b/src/zones/azext_zones/vendored_sdks/resourcegraph/py.typed new file mode 100644 index 00000000000..e5aff4f83af --- /dev/null +++ b/src/zones/azext_zones/vendored_sdks/resourcegraph/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. \ No newline at end of file diff --git a/src/zones/setup.cfg b/src/zones/setup.cfg new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/zones/setup.py b/src/zones/setup.py new file mode 100644 index 00000000000..402e474fd50 --- /dev/null +++ b/src/zones/setup.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + + +from codecs import open +from setuptools import setup, find_packages +try: + from azure_bdist_wheel import cmdclass +except ImportError: + from distutils import log as logger + logger.warn("Wheel is not available, disabling bdist_wheel hook") + +# TODO: Confirm this is the right version number you want and it matches your +# HISTORY.rst entry. +VERSION = '1.0.0b1' + +# The full list of classifiers is available at +# https://pypi.python.org/pypi?%3Aaction=list_classifiers +CLASSIFIERS = [ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'License :: OSI Approved :: MIT License', +] + +# TODO: Add any additional SDK dependencies here +DEPENDENCIES = [] + +with open('README.md', 'r', encoding='utf-8') as f: + README = f.read() +with open('HISTORY.rst', 'r', encoding='utf-8') as f: + HISTORY = f.read() + +setup( + name='zones', + version=VERSION, + description='Microsoft Azure Command-Line Tools Zones Extension', + author='Niels Buit', + author_email='nielsb@microsoft.com', + url='https://github.com/Azure/azure-cli-extensions/tree/master/src/zones', + long_description=README + '\n\n' + HISTORY, + license='MIT', + classifiers=CLASSIFIERS, + packages=find_packages(), + install_requires=DEPENDENCIES, + package_data={'azext_zones': ['azext_metadata.json']}, +)