From 0ad46009734e4f72c0584f0a00e9bca17dffe7bd Mon Sep 17 00:00:00 2001 From: Yugang Wang Date: Tue, 22 Mar 2022 04:12:36 -0700 Subject: [PATCH] Onboard command extension for Azure Managed Grafana service (#4495) * POC: update with command list to implement * consolidate control plane and dashboard commands * half done: make resource group and location optional * more commands and code style fixes * add a validator to workspace https://bugs.python.org/issue9334 * catch up a few TODOs * Consolidate data source CRUD commands with bug fixes and samples * add help * bug fixes on data source commands * fix style errors * fix bugs in data-source commands * clean up command help * upload whl file for tempoaray measures, before we publish it officially * update readme and bug fixes * update get_start doc * Update get_start,md * fix typos in get_start.md * add vendor SDK * new extension * new extension built from azdev * rename from ags to amg * add tag support * Update readme.rst * undo non related changes * rename readme to markdown * address lint error * address linter error * more fix towards command lint error * register the command module in a few common file * add import * support gallery import * use deep copy * address review feedback * set the minimum cli core version * fix a bug in 'az grafana user show' * Remove the 'id' on creating dashboard to prevent 'Not Found' error --- .github/CODEOWNERS | 4 +- src/amg/HISTORY.rst | 8 + src/amg/README.md | 55 + src/amg/azext_amg/__init__.py | 31 + src/amg/azext_amg/_client_factory.py | 11 + src/amg/azext_amg/_help.py | 212 +++ src/amg/azext_amg/_params.py | 57 + src/amg/azext_amg/_validators.py | 32 + src/amg/azext_amg/azext_metadata.json | 5 + src/amg/azext_amg/commands.py | 45 + src/amg/azext_amg/custom.py | 440 ++++++ src/amg/azext_amg/tests/__init__.py | 5 + src/amg/azext_amg/tests/latest/__init__.py | 5 + .../tests/latest/recordings/test_amg_e2e.yaml | 1303 +++++++++++++++++ .../tests/latest/test_amg_scenario.py | 39 + src/amg/azext_amg/vendored_sdks/__init__.py | 18 + .../azext_amg/vendored_sdks/_configuration.py | 68 + .../_dashboard_management_client.py | 97 ++ src/amg/azext_amg/vendored_sdks/_patch.py | 31 + src/amg/azext_amg/vendored_sdks/_vendor.py | 27 + src/amg/azext_amg/vendored_sdks/_version.py | 9 + .../azext_amg/vendored_sdks/aio/__init__.py | 15 + .../vendored_sdks/aio/_configuration.py | 67 + .../aio/_dashboard_management_client.py | 94 ++ src/amg/azext_amg/vendored_sdks/aio/_patch.py | 31 + .../vendored_sdks/aio/operations/__init__.py | 15 + .../aio/operations/_grafana_operations.py | 534 +++++++ .../aio/operations/_operations.py | 112 ++ .../vendored_sdks/models/__init__.py | 57 + .../_dashboard_management_client_enums.py | 67 + .../vendored_sdks/models/_models_py3.py | 623 ++++++++ .../vendored_sdks/operations/__init__.py | 15 + .../operations/_grafana_operations.py | 758 ++++++++++ .../vendored_sdks/operations/_operations.py | 137 ++ src/amg/dist/amg-0.1.0-py3-none-any.whl | Bin 0 -> 42653 bytes src/amg/setup.cfg | 0 src/amg/setup.py | 56 + src/service_name.json | 7 +- 38 files changed, 5088 insertions(+), 2 deletions(-) create mode 100644 src/amg/HISTORY.rst create mode 100644 src/amg/README.md create mode 100644 src/amg/azext_amg/__init__.py create mode 100644 src/amg/azext_amg/_client_factory.py create mode 100644 src/amg/azext_amg/_help.py create mode 100644 src/amg/azext_amg/_params.py create mode 100644 src/amg/azext_amg/_validators.py create mode 100644 src/amg/azext_amg/azext_metadata.json create mode 100644 src/amg/azext_amg/commands.py create mode 100644 src/amg/azext_amg/custom.py create mode 100644 src/amg/azext_amg/tests/__init__.py create mode 100644 src/amg/azext_amg/tests/latest/__init__.py create mode 100644 src/amg/azext_amg/tests/latest/recordings/test_amg_e2e.yaml create mode 100644 src/amg/azext_amg/tests/latest/test_amg_scenario.py create mode 100644 src/amg/azext_amg/vendored_sdks/__init__.py create mode 100644 src/amg/azext_amg/vendored_sdks/_configuration.py create mode 100644 src/amg/azext_amg/vendored_sdks/_dashboard_management_client.py create mode 100644 src/amg/azext_amg/vendored_sdks/_patch.py create mode 100644 src/amg/azext_amg/vendored_sdks/_vendor.py create mode 100644 src/amg/azext_amg/vendored_sdks/_version.py create mode 100644 src/amg/azext_amg/vendored_sdks/aio/__init__.py create mode 100644 src/amg/azext_amg/vendored_sdks/aio/_configuration.py create mode 100644 src/amg/azext_amg/vendored_sdks/aio/_dashboard_management_client.py create mode 100644 src/amg/azext_amg/vendored_sdks/aio/_patch.py create mode 100644 src/amg/azext_amg/vendored_sdks/aio/operations/__init__.py create mode 100644 src/amg/azext_amg/vendored_sdks/aio/operations/_grafana_operations.py create mode 100644 src/amg/azext_amg/vendored_sdks/aio/operations/_operations.py create mode 100644 src/amg/azext_amg/vendored_sdks/models/__init__.py create mode 100644 src/amg/azext_amg/vendored_sdks/models/_dashboard_management_client_enums.py create mode 100644 src/amg/azext_amg/vendored_sdks/models/_models_py3.py create mode 100644 src/amg/azext_amg/vendored_sdks/operations/__init__.py create mode 100644 src/amg/azext_amg/vendored_sdks/operations/_grafana_operations.py create mode 100644 src/amg/azext_amg/vendored_sdks/operations/_operations.py create mode 100644 src/amg/dist/amg-0.1.0-py3-none-any.whl create mode 100644 src/amg/setup.cfg create mode 100644 src/amg/setup.py diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a988612eff1..04e86c8ce86 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -226,6 +226,8 @@ /src/datamigration/ @ashutoshsuman99 +/src/amg/ @yugangw-msft + /src/confidentialledger/ @kairu-ms @lynshi -/src/quota/ @kairu-ms @ZengTaoxu +/src/quota/ @kairu-ms @ZengTaoxu \ No newline at end of file diff --git a/src/amg/HISTORY.rst b/src/amg/HISTORY.rst new file mode 100644 index 00000000000..8c34bccfff8 --- /dev/null +++ b/src/amg/HISTORY.rst @@ -0,0 +1,8 @@ +.. :changelog: + +Release History +=============== + +0.1.0 +++++++ +* Initial release. \ No newline at end of file diff --git a/src/amg/README.md b/src/amg/README.md new file mode 100644 index 00000000000..f3a6a8df8db --- /dev/null +++ b/src/amg/README.md @@ -0,0 +1,55 @@ +# Microsoft Azure CLI 'amg' Extension +This is an extension to manage Azure Manaaged Grafana instances + +## How to use ## +Install this extension using the below CLI command +``` +az extension add --name amg +``` + +## Included Features +### Create, show, and delete instances + +#### create an instance +*Examples:* +``` +az grafana create \ + -g MyResourceGroup \ + -n MyGrafanaInstance \ + --tags department=financial +``` + +#### delete an instance +*Examples:* +``` +az grafana delete \ + -n MyGrafanaInstance +``` + +### Configure folder, data sources and dashboard + +#### create a folder +*Examples:* +``` +az grafana folder create \ + -n MyGrafanaInstance \ + --title "Health KPI" +``` + +#### configure a data source +*Examples:* +``` +az grafana data-source create \ + -n MyGrafanaInstance \ + --definition ~/data-source-sql.json +``` + +#### Create a dashboard +*Examples:* +``` +az grafana dashboard create \ + -n MyGrafanaInstance \ + --folder "Health KPI" \ + --title "SQL status" \ + --definition ~/dashboard-sql.json +``` \ No newline at end of file diff --git a/src/amg/azext_amg/__init__.py b/src/amg/azext_amg/__init__.py new file mode 100644 index 00000000000..3940cb1b4ca --- /dev/null +++ b/src/amg/azext_amg/__init__.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 azure.cli.core import AzCommandsLoader + +from azext_amg._help import helps # pylint: disable=unused-import + + +class AmgCommandsLoader(AzCommandsLoader): + + def __init__(self, cli_ctx=None): + from azure.cli.core.commands import CliCommandType + amg_custom = CliCommandType( + operations_tmpl='azext_amg.custom#{}') + # pylint: disable=super-with-arguments + super(AmgCommandsLoader, self).__init__(cli_ctx=cli_ctx, + custom_command_type=amg_custom) + + def load_command_table(self, args): + from azext_amg.commands import load_command_table + load_command_table(self, args) + return self.command_table + + def load_arguments(self, command): + from azext_amg._params import load_arguments + load_arguments(self, command) + + +COMMAND_LOADER_CLS = AmgCommandsLoader diff --git a/src/amg/azext_amg/_client_factory.py b/src/amg/azext_amg/_client_factory.py new file mode 100644 index 00000000000..da746bc832a --- /dev/null +++ b/src/amg/azext_amg/_client_factory.py @@ -0,0 +1,11 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + + +def cf_amg(cli_ctx, *_): + # pylint: disable=unused-argument + from azure.cli.core.commands.client_factory import get_mgmt_service_client + from azext_amg.vendored_sdks import DashboardManagementClient + return get_mgmt_service_client(cli_ctx, DashboardManagementClient) diff --git a/src/amg/azext_amg/_help.py b/src/amg/azext_amg/_help.py new file mode 100644 index 00000000000..c70b58c3bb6 --- /dev/null +++ b/src/amg/azext_amg/_help.py @@ -0,0 +1,212 @@ +# 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['grafana'] = """ + type: group + short-summary: Commands to manage Azure Grafana instanced. + long-summary: For optimized experience, not all data plane Apis, documented at https://grafana.com/docs/grafana/latest/http_api/, are exposed. On coverage gap, please reach out to ad4g@microsoft.com +""" + +helps['grafana create'] = """ + type: command + short-summary: Create a Azure Managed Grafana instance. +""" + +helps['grafana list'] = """ + type: command + short-summary: List Azure Managed Grafana instances. +""" + +helps['grafana delete'] = """ + type: command + short-summary: Delete a Azure Managed Grafana instance. +""" + +helps['grafana show'] = """ + type: command + short-summary: Show details of a Azure Managed Grafana instance. +""" + +helps['grafana data-source'] = """ + type: group + short-summary: Commands to manage data sources of an instance. +""" + +helps['grafana data-source create'] = """ + type: command + short-summary: Create a data source. + examples: + - name: create a data source of Azure SQL + text: | + az grafana data-source create -n MyGrafana --definition '{ + "access": "proxy", + "database": "testdb", + "jsonData": { + "authenticationType": "SQL Server Authentication", + "encrypt": "false" + }, + "secureJsonData": { + "password": "verySecretPassword" + }, + "name": "Microsoft SQL Server", + "type": "mssql", + "url": "testsql.database.windows.net", + "user": "admin1" + }' +""" + + +helps['grafana data-source update'] = """ + type: command + short-summary: Update a data source. +""" + +helps['grafana data-source show'] = """ + type: command + short-summary: get details of a data source +""" + +helps['grafana data-source delete'] = """ + type: command + short-summary: delete a data source +""" + +helps['grafana data-source list'] = """ + type: command + short-summary: List all data sources of an instance. +""" + +helps['grafana data-source query'] = """ + type: command + short-summary: query a data source having backend implementation +""" + +helps['grafana dashboard'] = """ + type: group + short-summary: Commands to manage dashboards of an instance. +""" + +helps['grafana dashboard create'] = """ + type: command + short-summary: Create a new dashboard. + examples: + - name: Create a dashboard with definition in a json file. For quick start, clone from the output of "az grafana dashboard show", remove "id" and "uid", and apply changes. + text: | + az grafana dashboard create -g MyResourceGroup -n MyGrafana --title "My dashboard" --folder folder1 --definition '{ + "dashboard": { + "annotations": { + ... + }, + "panels": { + ... + } + }, + "message": "Create a new test dashboard" + }' +""" + +helps['grafana dashboard update'] = """ + type: command + short-summary: Update a dashboard. + examples: + - name: Update a dashboard with definition in a json file. For quick start, get existing configuration from "az grafana dashboard show", and apply changes. + "version" field need to be updated, and "overwrite" field should be true. + text: | + az grafana dashboard update -g MyResourceGroup -n MyGrafana --definition @c:\\temp\\dashboard.json +""" + +helps['grafana dashboard import'] = """ + type: command + short-summary: import a dashboard. + long-summary: CLI command will fill in required parameters for data sources if configured + examples: + - name: import the dashboard of "AKS Container Insights" from Grafana gallery. + text: | + az grafana dashboard import -g MyResourceGroup -n MyGrafana --definition 12180 + - name: import a dashboard from a file. + text: | + az grafana dashboard import -g MyResourceGroup -n MyGrafana --definition @c:\\temp\\dashboard.json +""" + +helps['grafana dashboard list'] = """ + type: command + short-summary: List all dashboards of an instance. + examples: + - name: Find the dashboard for K8s API Server and retrieve the unique identifier(in order to invoke "az grafana dashboard show" command) + text: | + az grafana dashboard list -g MyResourceGroup -n MyGrafana --query "[?contains(@.title, 'API server')].uid" +""" + +helps['grafana dashboard show'] = """ + type: command + short-summary: show the detail of a dashboard. + examples: + - name: Get details of a dashboard specified by an unique identifier(use "az grafana dashboard list" command to retrieve the uid) + text: | + az grafana dashboard show -g MyResourceGroup -n MyGrafana --dashboard VdrOA7jGz +""" + +helps['grafana dashboard delete'] = """ + type: command + short-summary: delete a dashboard + examples: + - name: Delete a dashboard specified by an unique identifier(use "az grafana dashboard list" command to retrieve the uid) + text: | + az grafana dashboard delete -g MyResourceGroup -n MyGrafana --dashboard VdrOA7jGz +""" + +helps['grafana folder'] = """ + type: group + short-summary: Commands to manage folders of an instance. +""" + +helps['grafana folder create'] = """ + type: command + short-summary: create a new folder. +""" + +helps['grafana folder show'] = """ + type: command + short-summary: show the details of a folder. +""" + +helps['grafana folder list'] = """ + type: command + short-summary: list all folders of an instance. +""" + +helps['grafana folder update'] = """ + type: command + short-summary: update a folder. +""" + +helps['grafana folder delete'] = """ + type: command + short-summary: delete a folder. +""" + +helps['grafana user'] = """ + type: group + short-summary: Commands to manage users of an instance. +""" + +helps['grafana user actual-user'] = """ + type: command + short-summary: show details of current user. +""" + +helps['grafana user list'] = """ + type: command + short-summary: list users. +""" + +helps['grafana user show'] = """ + type: command + short-summary: show detail of a user. +""" diff --git a/src/amg/azext_amg/_params.py b/src/amg/azext_amg/_params.py new file mode 100644 index 00000000000..dcb984ffe13 --- /dev/null +++ b/src/amg/azext_amg/_params.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. +# -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long + + +def load_arguments(self, _): + + from knack.arguments import CLIArgumentType + from azure.cli.core.commands.parameters import tags_type, get_three_state_flag + from azure.cli.core.commands.validators import get_default_location_from_resource_group + from ._validators import process_missing_resource_group_parameter + + grafana_name_type = CLIArgumentType(options_list="--grafana-name", + help="Name of the Azure Managed Dashboard for Grafana.", + id_part="name") + + with self.argument_context("grafana") as c: + c.argument("tags", tags_type) + c.argument("location", validator=get_default_location_from_resource_group) + c.argument("grafana_name", grafana_name_type, options_list=["--name", "-n"], id_part=None, validator=process_missing_resource_group_parameter) + c.argument("id", help=("The identifier (id) of a dashboard/data source is an auto-incrementing " + "numeric value and is only unique per Grafana install.")) + c.argument("folder", help="id, uid, title which can identify a folder. CLI will search in the order of id, uid, and title, till finds a match") + + with self.argument_context("grafana create") as c: + c.argument("grafana_name", grafana_name_type, options_list=["--name", "-n"], validator=None) + c.argument("skip_system_assigned_identity", options_list=["-s", "--skip-system-assigned-identity"], arg_type=get_three_state_flag(), help="Do not enable system assigned identity") + c.argument("skip_role_assignments", arg_type=get_three_state_flag(), help="Do not create role assignments for managed identity and the current login user") + + with self.argument_context("grafana dashboard") as c: + c.argument("uid", options_list=["--dashboard"], help="dashboard uid") + c.argument("definition", help="The complete dashboard model in json string, a path or url to a file with such content") + c.argument("title", help="title of a dashboard") + c.argument('overwrite', arg_type=get_three_state_flag(), help='Overwrite a dashboard with same uid') + + with self.argument_context("grafana dashboard import") as c: + c.argument("definition", help="The complete dashboard model in json string, Grafana gallery id, a path or url to a file with such content") + + with self.argument_context("grafana data-source") as c: + c.argument("data_source", help="name, id, uid which can identify a data source. CLI will search in the order of name, id, and uid, till finds a match") + c.argument("definition", help="json string with data source definition, or a path to a file with such content") + + with self.argument_context("grafana data-source query") as c: + c.argument("conditions", nargs="+", help="space-separated condition in a format of `=`") + c.argument("time_from", options_list=["--from"], help="start time in iso 8601, e.g. '2022-01-02T16:15:00'. Default: 1 hour early") + c.argument("time_to", options_list=["--to"], help="end time in iso 8601, e.g. '2022-01-02T17:15:00'. Default: current time ") + c.argument("max_data_points", help="Maximum amount of data points that dashboard panel can render") + c.argument("query_format", help="format of the resule, e.g. table, time_series") + c.argument("internal_ms", help="The time interval in milliseconds of time series") + + with self.argument_context("grafana folder") as c: + c.argument("title", help="title of the folder") + + with self.argument_context("grafana user") as c: + c.argument("user", help="user login name or email") diff --git a/src/amg/azext_amg/_validators.py b/src/amg/azext_amg/_validators.py new file mode 100644 index 00000000000..e905c58afe1 --- /dev/null +++ b/src/amg/azext_amg/_validators.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 msrestazure.tools import parse_resource_id + +from knack.util import CLIError + +from azure.cli.core.commands.validators import get_default_location_from_resource_group, validate_tags +from azure.cli.core.commands.client_factory import get_mgmt_service_client +from azure.cli.core.profiles import ResourceType + + +def process_grafana_create_namespace(cmd, namespace): + validate_tags(namespace) + if not namespace.location: + get_default_location_from_resource_group(cmd, namespace) + + +def process_missing_resource_group_parameter(cmd, namespace): + if not namespace.resource_group_name and namespace.grafana_name: + client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) + resources = client.resources.list(filter="resourceType eq 'Microsoft.Dashboard/grafana'") + resources = list(resources) + match = next((i for i in resources if i.name == namespace.grafana_name), None) + if match: + namespace.resource_group_name = parse_resource_id(match.id)["resource_group"] + else: + raise CLIError((f"Not able to find the Grafana instance: '{namespace.grafana_name}'. Please " + f"correct the name, or provide resource group name, or set CLI " + f"subscription the workspace belongs to")) diff --git a/src/amg/azext_amg/azext_metadata.json b/src/amg/azext_amg/azext_metadata.json new file mode 100644 index 00000000000..044f0f3f3f3 --- /dev/null +++ b/src/amg/azext_amg/azext_metadata.json @@ -0,0 +1,5 @@ +{ + "azext.isPreview": true, + "azext.minCliCoreVersion": "2.30.0", + "azext.maxCliCoreVersion": "2.99.0" +} \ No newline at end of file diff --git a/src/amg/azext_amg/commands.py b/src/amg/azext_amg/commands.py new file mode 100644 index 00000000000..515b0f408b2 --- /dev/null +++ b/src/amg/azext_amg/commands.py @@ -0,0 +1,45 @@ +# -------------------------------------------------------------------------------------------- +# 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 ._validators import process_grafana_create_namespace + + +def load_command_table(self, _): + + with self.command_group('grafana', is_preview=True) as g: + g.custom_command('create', 'create_grafana', validator=process_grafana_create_namespace) + g.custom_command('delete', 'delete_grafana', confirmation=True) + g.custom_command('list', 'list_grafana') + g.custom_show_command('show', 'show_grafana') + + with self.command_group('grafana dashboard') as g: + g.custom_command('create', 'create_dashboard') + g.custom_command('delete', 'delete_dashboard') + g.custom_command('list', 'list_dashboards') + g.custom_show_command('show', 'show_dashboard') + g.custom_command('update', 'update_dashboard') + g.custom_command('import', 'import_dashboard') + + with self.command_group('grafana data-source') as g: + g.custom_command('create', 'create_data_source') + g.custom_command('list', 'list_data_sources') + g.custom_show_command('show', 'show_data_source') + g.custom_command('delete', 'delete_data_source') + g.custom_command('query', 'query_data_source') + g.custom_command('update', 'update_data_source') + + with self.command_group('grafana folder') as g: + g.custom_command('create', 'create_folder') + g.custom_command('list', 'list_folders') + g.custom_show_command('show', 'show_folder') + g.custom_command('delete', 'delete_folder') + g.custom_command('update', 'update_folder') + + with self.command_group('grafana user') as g: + g.custom_command('list', 'list_users') + g.custom_show_command('show', 'show_user') + g.custom_command('actual-user', 'get_actual_user') diff --git a/src/amg/azext_amg/custom.py b/src/amg/azext_amg/custom.py new file mode 100644 index 00000000000..63a90f6b552 --- /dev/null +++ b/src/amg/azext_amg/custom.py @@ -0,0 +1,440 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import json +import requests + +from msrestazure.azure_exceptions import CloudError + +from knack.log import get_logger + +from azure.cli.core.commands.client_factory import get_mgmt_service_client, get_subscription_id +from azure.cli.core.profiles import ResourceType, get_sdk +from azure.cli.core.util import should_disable_connection_verify +from azure.cli.core.azclierror import ArgumentUsageError, CLIInternalError + +from ._client_factory import cf_amg + +logger = get_logger(__name__) + + +grafana_endpoints = {} + + +def create_grafana(cmd, resource_group_name, grafana_name, + location=None, skip_system_assigned_identity=False, skip_role_assignments=False, tags=None): + from azure.cli.core.commands.arm import resolve_role_id + from azure.cli.core.commands import LongRunningOperation + client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) + resource = { + "sku": { + "name": "standard" + }, + "location": location, + "identity": None if skip_system_assigned_identity else {"type": "SystemAssigned"}, + "tags": tags + } + poller = client.resources.begin_create_or_update(resource_group_name, "Microsoft.Dashboard", "", + "grafana", grafana_name, "2021-09-01-preview", resource) + + if skip_role_assignments: + return poller + resource = LongRunningOperation(cmd.cli_ctx)(poller) + + logger.warning("Grafana instance of '%s' was created. Now creating default role assignments for its " + "managed identity and current CLI user", grafana_name) + + subscription_scope = '/subscriptions/' + client._config.subscription_id # pylint: disable=protected-access + + user_principal_id = _get_login_account_principal_id(cmd.cli_ctx) + grafana_admin_role_id = resolve_role_id(cmd.cli_ctx, "Grafana Admin", subscription_scope) + _create_role_assignment(cmd.cli_ctx, user_principal_id, grafana_admin_role_id, resource.id) + + if resource.identity: + monitoring_reader_role_id = resolve_role_id(cmd.cli_ctx, "Monitoring Reader", subscription_scope) + _create_role_assignment(cmd.cli_ctx, resource.identity.principal_id, monitoring_reader_role_id, + subscription_scope) + + return resource + + +def _get_login_account_principal_id(cli_ctx): + from azure.cli.core._profile import Profile + from azure.graphrbac import GraphRbacManagementClient + profile = Profile(cli_ctx=cli_ctx) + cred, _, tenant_id = profile.get_login_credentials( + resource=cli_ctx.cloud.endpoints.active_directory_graph_resource_id) + client = GraphRbacManagementClient(cred, tenant_id, + base_url=cli_ctx.cloud.endpoints.active_directory_graph_resource_id) + assignee = profile.get_current_account_user() + result = list(client.users.list(filter=f"userPrincipalName eq '{assignee}'")) + if not result: + result = list(client.service_principals.list( + filter=f"servicePrincipalNames/any(c:c eq '{assignee}')")) + if not result: + raise CLIInternalError((f"Failed to retrieve principal id for '{assignee}', which is needed to create a " + f"role assignment")) + return result[0].object_id + + +def _create_role_assignment(cli_ctx, principal_id, role_definition_id, scope): + import time + import uuid + assignments_client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_AUTHORIZATION).role_assignments + RoleAssignmentCreateParameters = get_sdk(cli_ctx, ResourceType.MGMT_AUTHORIZATION, + 'RoleAssignmentCreateParameters', mod='models', + operation_group='role_assignments') + parameters = RoleAssignmentCreateParameters(role_definition_id=role_definition_id, principal_id=principal_id) + + logger.info("Creating an assignment with a role '%s' on the scope of '%s'", role_definition_id, scope) + retry_times = 36 + assignment_name = uuid.uuid4() + for retry_time in range(0, retry_times): + try: + assignments_client.create(scope=scope, role_assignment_name=assignment_name, + parameters=parameters) + break + except CloudError as ex: + if 'role assignment already exists' in ex.message: + logger.info('Role assignment already exists') + break + if retry_time < retry_times and ' does not exist in the directory ' in ex.message: + time.sleep(5) + logger.warning('Retrying role assignment creation: %s/%s', retry_time + 1, + retry_times) + continue + raise + + +def list_grafana(cmd, resource_group_name=None): + client = cf_amg(cmd.cli_ctx) + if resource_group_name: + return client.grafana.list_by_resource_group(resource_group_name) + return client.grafana.list() + + +def show_grafana(cmd, grafana_name, resource_group_name=None): + client = cf_amg(cmd.cli_ctx) + return client.grafana.get(resource_group_name, grafana_name) + + +def delete_grafana(cmd, grafana_name, resource_group_name=None): + client = get_mgmt_service_client(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) + return client.resources.begin_delete(resource_group_name, "Microsoft.Dashboard", + "", "grafana", grafana_name, "2021-09-01-preview") + + +def show_dashboard(cmd, grafana_name, uid, resource_group_name=None): + response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/dashboards/uid/" + uid) + return json.loads(response.content) + + +def list_dashboards(cmd, grafana_name, resource_group_name=None): + response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/search?type=dash-db") + return json.loads(response.content) + + +def create_dashboard(cmd, grafana_name, definition, title=None, folder=None, resource_group_name=None, overwrite=None): + data = _try_load_dashboard_definition(cmd, resource_group_name, grafana_name, definition, for_import=False) + if "dashboard" in data: + payload = data + else: + logger.info("Adjust input by adding 'dashboard' field") + payload = {} + payload["dashboard"] = data + + if title: + payload['dashboard']['title'] = title + + if folder: + folder = _find_folder(cmd, resource_group_name, grafana_name, folder) + payload['folderId'] = folder["id"] + + payload["overwrite"] = overwrite or False + + if "id" in payload["dashboard"]: + logger.warning("Removing 'id' from dashboard to prevent the error of 'Not Found'") + del payload["dashboard"]["id"] + + response = _send_request(cmd, resource_group_name, grafana_name, "post", "/api/dashboards/db", + payload) + return json.loads(response.content) + + +def update_dashboard(cmd, grafana_name, definition, folder=None, resource_group_name=None, overwrite=None): + return create_dashboard(cmd, grafana_name, definition, folder=folder, + resource_group_name=resource_group_name, + overwrite=overwrite) + + +def import_dashboard(cmd, grafana_name, definition, folder=None, resource_group_name=None, overwrite=None): + import copy + data = _try_load_dashboard_definition(cmd, resource_group_name, grafana_name, definition, for_import=True) + if "dashboard" in data: + payload = data + else: + logger.info("Adjust input by adding 'dashboard' field") + payload = {} + payload["dashboard"] = data + + if folder: + folder = _find_folder(cmd, resource_group_name, grafana_name, folder) + payload['folderId'] = folder["id"] + + payload["overwrite"] = overwrite or False + + payload["inputs"] = [] + + # provide parameter values for datasource + data_sources = list_data_sources(cmd, grafana_name, resource_group_name) + for parameter in payload["dashboard"].get('__inputs', []): + if parameter.get("type") == "datasource": + match = next((d for d in data_sources if d["type"] == parameter["pluginId"]), None) + if match: + clone = copy.deepcopy(parameter) + clone["value"] = match["uid"] + payload["inputs"].append(clone) + else: + logger.warning("No data source was found matching the required parameter of %s", parameter['pluginId']) + + response = _send_request(cmd, resource_group_name, grafana_name, "post", "/api/dashboards/import", + payload) + return json.loads(response.content) + + +def _try_load_dashboard_definition(cmd, resource_group_name, grafana_name, definition, for_import): + import re + + if for_import: + try: # see whether it is a gallery id + int(definition) + response = _send_request(cmd, resource_group_name, grafana_name, "get", + "/api/gnet/dashboards/" + definition) + return json.loads(response.content)["json"] + except ValueError: + pass + + if re.match(r"^[a-z]+://", definition.lower()): + response = requests.get(definition, verify=(not should_disable_connection_verify())) + if response.status_code == 200: + definition = json.loads(response.content.decode()) + else: + raise ArgumentUsageError(f"Failed to dashboard definition from '{definition}'. Error: '{response}'.") + else: + definition = json.loads(_try_load_file_content(definition)) + + return definition + + +def delete_dashboard(cmd, grafana_name, uid, resource_group_name=None): + _send_request(cmd, resource_group_name, grafana_name, "delete", "/api/dashboards/uid/" + uid) + + +def create_data_source(cmd, grafana_name, definition, resource_group_name=None): + definition = _try_load_file_content(definition) + payload = json.loads(definition) + response = _send_request(cmd, resource_group_name, grafana_name, "post", "/api/datasources", payload) + return json.loads(response.content) + + +def show_data_source(cmd, grafana_name, data_source, resource_group_name=None): + return _find_data_source(cmd, resource_group_name, grafana_name, data_source) + + +def delete_data_source(cmd, grafana_name, data_source, resource_group_name=None): + data = _find_data_source(cmd, resource_group_name, grafana_name, data_source) + _send_request(cmd, resource_group_name, grafana_name, "delete", "/api/datasources/uid/" + data["uid"]) + + +def list_data_sources(cmd, grafana_name, resource_group_name=None): + response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/datasources") + return json.loads(response.content) + + +def update_data_source(cmd, grafana_name, data_source, definition, resource_group_name=None): + definition = _try_load_file_content(definition) + data = _find_data_source(cmd, resource_group_name, grafana_name, data_source) + response = _send_request(cmd, resource_group_name, grafana_name, "put", "/api/datasources/" + str(data['id']), + json.loads(definition)) + return json.loads(response.content) + + +def create_folder(cmd, grafana_name, title, resource_group_name=None): + payload = { + "title": title + } + response = _send_request(cmd, resource_group_name, grafana_name, "post", "/api/folders", payload) + return json.loads(response.content) + + +def list_folders(cmd, grafana_name, resource_group_name=None): + response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/folders") + return json.loads(response.content) + + +def update_folder(cmd, grafana_name, folder, title, resource_group_name=None): + f = show_folder(cmd, grafana_name, folder, resource_group_name) + version = f['version'] + data = { + "title": title, + "version": int(version) + } + response = _send_request(cmd, resource_group_name, grafana_name, "put", "/api/folders/" + f["uid"], data) + return json.loads(response.content) + + +def show_folder(cmd, grafana_name, folder, resource_group_name=None): + return _find_folder(cmd, resource_group_name, grafana_name, folder) + + +def delete_folder(cmd, grafana_name, folder, resource_group_name=None): + data = _find_folder(cmd, resource_group_name, grafana_name, folder) + _send_request(cmd, resource_group_name, grafana_name, "delete", "/api/folders/" + data['uid']) + + +def _find_folder(cmd, resource_group_name, grafana_name, folder): + response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/folders/id/" + folder, + raise_for_error_status=False) + if response.status_code >= 400 or not json.loads(response.content)['uid']: + response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/folders/" + folder, + raise_for_error_status=False) + if response.status_code >= 400: + response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/folders") + if response.status_code >= 400: + raise ArgumentUsageError(f"Could't find the folder '{folder}'. Ex: {response.status_code}") + result = json.loads(response.content) + result = [f for f in result if f["title"] == folder] + if len(result) == 0: + raise ArgumentUsageError(f"Could't find the folder '{folder}'. Ex: {response.status_code}") + if len(result) > 1: + raise ArgumentUsageError((f"More than one folder has the same title of '{folder}'. Please use other " + f"unique identifiers")) + return result[0] + + return json.loads(response.content) + + +def get_actual_user(cmd, grafana_name, resource_group_name=None): + response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/user") + return json.loads(response.content) + + +def list_users(cmd, grafana_name, resource_group_name=None): + response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/org/users") + return json.loads(response.content) + + +def show_user(cmd, grafana_name, user, resource_group_name=None): + users = list_users(cmd, grafana_name, resource_group_name=resource_group_name) + match = next((u for u in users if u['name'].lower() == user.lower()), None) + + if match: + return match + raise ArgumentUsageError(f"Could't find the user '{user}'") + + +def query_data_source(cmd, grafana_name, data_source, time_from=None, time_to=None, + max_data_points=100, internal_ms=1000, query_format=None, + conditions=None, resource_group_name=None): + import datetime + import time + from dateutil import parser + right_now = datetime.datetime.now() + + if time_from: + time_from = parser.parse(time_from) + else: + time_from = right_now - datetime.timedelta(hours=1) + time_from_epoch = str(time.mktime(time_from.timetuple()) * 1000) + + if time_to: + time_to = parser.parse(time_to) + else: + time_to = right_now + time_to_epoch = str(time.mktime(time_to.timetuple()) * 1000) + + data_source_id = _find_data_source(cmd, resource_group_name, grafana_name, data_source)["id"] + + data = { + "from": time_from_epoch, + "to": time_to_epoch, + "queries": [{ + "intervalMs": internal_ms, + "maxDataPoints": max_data_points, + "datasourceId": data_source_id, + "format": query_format or "time_series", + "refId": "A" + }] + } + + if conditions: + for c in conditions: + k, v = c.split("=", 1) + data["queries"][0][k] = v + + response = _send_request(cmd, resource_group_name, grafana_name, "post", "/api/ds/query", data) + return json.loads(response.content) + + +def _find_data_source(cmd, resource_group_name, grafana_name, data_source): + response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/datasources/name/" + data_source, + raise_for_error_status=False) + if response.status_code >= 400: + response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/datasources/" + data_source, + raise_for_error_status=False) + if response.status_code >= 400: + response = _send_request(cmd, resource_group_name, grafana_name, + "get", "/api/datasources/uid/" + data_source, + raise_for_error_status=False) + if response.status_code >= 400: + raise ArgumentUsageError(f"Couldn't found data source {data_source}. Ex: {response.status_code}") + return json.loads(response.content) + + +# For UX: we accept a file path for complex payload such as dashboard/data-source definition +def _try_load_file_content(file_content): + import os + potentail_file_path = os.path.expanduser(file_content) + if os.path.exists(potentail_file_path): + from azure.cli.core.util import read_file_content + file_content = read_file_content(potentail_file_path) + return file_content + + +def _send_request(cmd, resource_group_name, grafana_name, http_method, path, body=None, raise_for_error_status=True): + endpoint = grafana_endpoints.get(grafana_name) + if not endpoint: + grafana = show_grafana(cmd, grafana_name, resource_group_name) + endpoint = grafana.properties.endpoint + grafana_endpoints[grafana_name] = endpoint + + from azure.cli.core._profile import Profile + profile = Profile(cli_ctx=cmd.cli_ctx) + # this might be a cross tenant scenario, so pass subscription to get_raw_token + subscription = get_subscription_id(cmd.cli_ctx) + amg_first_party_app = ("7f525cdc-1f08-4afa-af7c-84709d42f5d3" + if "-ppe." in cmd.cli_ctx.cloud.endpoints.active_directory + else "ce34e7e5-485f-4d76-964f-b3d2b16d1e4f") + creds, _, _ = profile.get_raw_token(subscription=subscription, + resource=amg_first_party_app) + + headers = { + "content-type": "application/json", + "authorization": "Bearer " + creds[1] + } + + # TODO: handle re-try on 429 + response = requests.request(http_method, + url=endpoint + path, + headers=headers, + json=body, + timeout=60, + verify=(not should_disable_connection_verify())) + if response.status_code >= 400: + if raise_for_error_status: + logger.warning(str(response.content)) + response.raise_for_status() + # TODO: log headers, requests and response + return response diff --git a/src/amg/azext_amg/tests/__init__.py b/src/amg/azext_amg/tests/__init__.py new file mode 100644 index 00000000000..2dcf9bb68b3 --- /dev/null +++ b/src/amg/azext_amg/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/amg/azext_amg/tests/latest/__init__.py b/src/amg/azext_amg/tests/latest/__init__.py new file mode 100644 index 00000000000..2dcf9bb68b3 --- /dev/null +++ b/src/amg/azext_amg/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/amg/azext_amg/tests/latest/recordings/test_amg_e2e.yaml b/src/amg/azext_amg/tests/latest/recordings/test_amg_e2e.yaml new file mode 100644 index 00000000000..0c2582e8ac6 --- /dev/null +++ b/src/amg/azext_amg/tests/latest/recordings/test_amg_e2e.yaml @@ -0,0 +1,1303 @@ +interactions: +- request: + body: '{"location": "westeurope", "tags": {"foo": "doo"}, "sku": {"name": "standard"}, + "identity": {"type": "SystemAssigned"}}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + Content-Length: + - '119' + Content-Type: + - application/json + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: PUT + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","name":"clitestamg","type":"microsoft.dashboard/grafana","sku":{"name":"standard"},"location":"westeurope","tags":{"foo":"doo"},"systemData":{"createdBy":"yugangw@microsoft.com","createdByType":"User","createdAt":"2022-03-06T19:05:53.5783015Z","lastModifiedBy":"yugangw@microsoft.com","lastModifiedByType":"User","lastModifiedAt":"2022-03-06T19:05:53.5783015Z"},"identity":{"principalId":"1112f52a-4439-44af-9ec2-7989874c0498","tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47","type":"SystemAssigned"},"properties":{"provisioningState":"Accepted","grafanaVersion":null,"endpoint":"https://clitestamg.weu.azgrafana.io","zoneRedundancy":"Disabled"}}' + headers: + api-supported-versions: + - 2021-09-01-preview + azure-asyncoperation: + - https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + cache-control: + - no-cache + content-length: + - '790' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:05:56 GMT + etag: + - '"8f00739e-0000-0d00-0000-622506140000"' + expires: + - '-1' + location: + - https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + pragma: + - no-cache + request-context: + - appId=cid-v1:54fcb76e-7bac-4b3e-96f8-8868676d3826 + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-ms-providerhub-traffic: + - 'True' + x-ms-ratelimit-remaining-subscription-writes: + - '1199' + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Accepted","startTime":"2022-03-06T19:05:56.1407573Z"}' + headers: + cache-control: + - no-cache + content-length: + - '504' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:06:26 GMT + etag: + - '"82005e6a-0000-0d00-0000-622506140000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Accepted","startTime":"2022-03-06T19:05:56.1407573Z"}' + headers: + cache-control: + - no-cache + content-length: + - '504' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:06:57 GMT + etag: + - '"82005e6a-0000-0d00-0000-622506140000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Accepted","startTime":"2022-03-06T19:05:56.1407573Z"}' + headers: + cache-control: + - no-cache + content-length: + - '504' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:07:27 GMT + etag: + - '"82005e6a-0000-0d00-0000-622506140000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Accepted","startTime":"2022-03-06T19:05:56.1407573Z"}' + headers: + cache-control: + - no-cache + content-length: + - '504' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:07:57 GMT + etag: + - '"82005e6a-0000-0d00-0000-622506140000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Accepted","startTime":"2022-03-06T19:05:56.1407573Z"}' + headers: + cache-control: + - no-cache + content-length: + - '504' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:08:27 GMT + etag: + - '"82005e6a-0000-0d00-0000-622506140000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Accepted","startTime":"2022-03-06T19:05:56.1407573Z"}' + headers: + cache-control: + - no-cache + content-length: + - '504' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:08:57 GMT + etag: + - '"82005e6a-0000-0d00-0000-622506140000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Accepted","startTime":"2022-03-06T19:05:56.1407573Z"}' + headers: + cache-control: + - no-cache + content-length: + - '504' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:09:29 GMT + etag: + - '"82005e6a-0000-0d00-0000-622506140000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Accepted","startTime":"2022-03-06T19:05:56.1407573Z"}' + headers: + cache-control: + - no-cache + content-length: + - '504' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:09:59 GMT + etag: + - '"82005e6a-0000-0d00-0000-622506140000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Accepted","startTime":"2022-03-06T19:05:56.1407573Z"}' + headers: + cache-control: + - no-cache + content-length: + - '504' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:10:29 GMT + etag: + - '"82005e6a-0000-0d00-0000-622506140000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"348cd2bc-114b-49f5-bf1d-9acb1eb76476*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Succeeded","startTime":"2022-03-06T19:05:56.1407573Z","endTime":"2022-03-06T19:10:38.0793744Z","error":{},"properties":null}' + headers: + cache-control: + - no-cache + content-length: + - '575' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:10:59 GMT + etag: + - '"8200a96d-0000-0d00-0000-6225072e0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana create + Connection: + - keep-alive + ParameterSetName: + - -g -n -l --tags --skip-role-assignments + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","name":"clitestamg","type":"microsoft.dashboard/grafana","sku":{"name":"Standard"},"location":"westeurope","tags":{"foo":"doo"},"systemData":{"createdBy":"yugangw@microsoft.com","createdByType":"User","createdAt":"2022-03-06T19:05:53.5783015Z","lastModifiedBy":"yugangw@microsoft.com","lastModifiedByType":"User","lastModifiedAt":"2022-03-06T19:05:53.5783015Z"},"identity":{"principalId":"1112f52a-4439-44af-9ec2-7989874c0498","tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47","type":"SystemAssigned"},"properties":{"provisioningState":"Succeeded","grafanaVersion":"8.4.1","endpoint":"https://clitestamg.weu.azgrafana.io","zoneRedundancy":"Disabled"}}' + headers: + cache-control: + - no-cache + content-length: + - '794' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:11:00 GMT + etag: + - '"8f0051a2-0000-0d00-0000-6225072e0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + x-ms-providerhub-traffic: + - 'True' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana list + Connection: + - keep-alive + ParameterSetName: + - -g + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-mgmt-dashboard/1.0.0b1 Python/3.8.8 (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana?api-version=2021-09-01-preview + response: + body: + string: '{"value":[{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","name":"clitestamg","type":"microsoft.dashboard/grafana","sku":{"name":"Standard"},"location":"westeurope","tags":{"foo":"doo"},"systemData":{"createdBy":"yugangw@microsoft.com","createdByType":"User","createdAt":"2022-03-06T19:05:53.5783015Z","lastModifiedBy":"yugangw@microsoft.com","lastModifiedByType":"User","lastModifiedAt":"2022-03-06T19:05:53.5783015Z"},"identity":{"principalId":"1112f52a-4439-44af-9ec2-7989874c0498","tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47","type":"SystemAssigned"},"properties":{"provisioningState":"Succeeded","grafanaVersion":"8.4.1","endpoint":"https://clitestamg.weu.azgrafana.io","zoneRedundancy":"Disabled"}}]}' + headers: + cache-control: + - no-cache + content-length: + - '806' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:11:02 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + x-ms-original-request-ids: + - f0d5c86c-28c9-418b-9387-e33f57984498 + - faf967bf-a804-400b-ab2e-c7173d24aa0a + - a7937dc1-2511-4272-bbc1-9e71e65da2e5 + - 17bb15fe-d548-4ea0-9b99-866bf8f2d3b0 + - 42e53806-1812-4704-8089-6cd88018f139 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana list + Connection: + - keep-alive + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-mgmt-dashboard/1.0.0b1 Python/3.8.8 (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Dashboard/grafana?api-version=2021-09-01-preview + response: + body: + string: '{"value":[{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/yugangw/providers/Microsoft.Dashboard/grafana/yugangweus2e","name":"yugangweus2e","type":"microsoft.dashboard/grafana","sku":{"name":"Standard"},"location":"eastus2euap","tags":{},"systemData":{"createdBy":"yugangw@microsoft.com","createdByType":"User","createdAt":"2022-03-04T03:45:10.1825533Z","lastModifiedBy":"yugangw@microsoft.com","lastModifiedByType":"User","lastModifiedAt":"2022-03-04T03:45:10.1825533Z"},"identity":{"principalId":"f462ffcd-951a-4521-82b0-0cff25c5c18a","tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47","type":"SystemAssigned"},"properties":{"provisioningState":"Succeeded","grafanaVersion":"8.4.1","endpoint":"https://yugangweus2e.eus2e.azgrafana.io","zoneRedundancy":"Disabled"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/yugangw/providers/Microsoft.Dashboard/grafana/yugangwcus3","name":"yugangwcus3","type":"microsoft.dashboard/grafana","sku":{"name":"Standard"},"location":"centraluseuap","tags":{},"systemData":{"createdBy":"yugangw@microsoft.com","createdByType":"User","createdAt":"2022-02-26T04:13:32.6306926Z","lastModifiedBy":"ce34e7e5-485f-4d76-964f-b3d2b16d1e4f","lastModifiedByType":"Application","lastModifiedAt":"2022-02-26T04:17:52.2265322Z"},"identity":{"principalId":"a2bd0803-4837-42e5-a1b8-d63bfa4f6a90","tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47","type":"SystemAssigned"},"properties":{"provisioningState":"Succeeded","grafanaVersion":"8.4.1","endpoint":"https://yugangwcus3.cuse.azgrafana.io","zoneRedundancy":"Disabled"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","name":"clitestamg","type":"microsoft.dashboard/grafana","sku":{"name":"Standard"},"location":"westeurope","tags":{"foo":"doo"},"systemData":{"createdBy":"yugangw@microsoft.com","createdByType":"User","createdAt":"2022-03-06T19:05:53.5783015Z","lastModifiedBy":"yugangw@microsoft.com","lastModifiedByType":"User","lastModifiedAt":"2022-03-06T19:05:53.5783015Z"},"identity":{"principalId":"1112f52a-4439-44af-9ec2-7989874c0498","tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47","type":"SystemAssigned"},"properties":{"provisioningState":"Succeeded","grafanaVersion":"8.4.1","endpoint":"https://clitestamg.weu.azgrafana.io","zoneRedundancy":"Disabled"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/yugangw/providers/Microsoft.Dashboard/grafana/yugangweus","name":"yugangweus","type":"microsoft.dashboard/grafana","sku":{"name":"Standard"},"location":"eastus","tags":{"doo":"foo"},"systemData":{"createdBy":"yugangw@microsoft.com","createdByType":"User","createdAt":"2022-03-06T18:29:10.4356225Z","lastModifiedBy":"yugangw@microsoft.com","lastModifiedByType":"User","lastModifiedAt":"2022-03-06T18:29:10.4356225Z"},"properties":{"provisioningState":"Succeeded","grafanaVersion":"8.4.1","endpoint":"https://yugangweus.eus.azgrafana.io","zoneRedundancy":"Disabled"}}]}' + headers: + cache-control: + - no-cache + content-length: + - '3030' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:11:03 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + x-ms-original-request-ids: + - f0472afa-d6bf-41be-891b-9bb0a7b50d13 + - 6040023f-5cdd-4c49-8bd6-ee2dcc37c1d1 + - 4ff9f9e4-803e-4e44-85b7-3b2aef5c0813 + - cc127ace-79ee-4663-ab05-63ddd2bebd63 + - 6707790a-1247-41f9-b964-abfc71a91c10 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana show + Connection: + - keep-alive + ParameterSetName: + - -g -n + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-mgmt-dashboard/1.0.0b1 Python/3.8.8 (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","name":"clitestamg","type":"microsoft.dashboard/grafana","sku":{"name":"Standard"},"location":"westeurope","tags":{"foo":"doo"},"systemData":{"createdBy":"yugangw@microsoft.com","createdByType":"User","createdAt":"2022-03-06T19:05:53.5783015Z","lastModifiedBy":"yugangw@microsoft.com","lastModifiedByType":"User","lastModifiedAt":"2022-03-06T19:05:53.5783015Z"},"identity":{"principalId":"1112f52a-4439-44af-9ec2-7989874c0498","tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47","type":"SystemAssigned"},"properties":{"provisioningState":"Succeeded","grafanaVersion":"8.4.1","endpoint":"https://clitestamg.weu.azgrafana.io","zoneRedundancy":"Disabled"}}' + headers: + cache-control: + - no-cache + content-length: + - '794' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:11:04 GMT + etag: + - '"8f0051a2-0000-0d00-0000-6225072e0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + x-ms-providerhub-traffic: + - 'True' + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + Content-Length: + - '0' + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: DELETE + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg?api-version=2021-09-01-preview + response: + body: + string: 'null' + headers: + api-supported-versions: + - 2021-09-01-preview + azure-asyncoperation: + - https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + cache-control: + - no-cache + content-length: + - '4' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:11:06 GMT + etag: + - '"8f00c3a2-0000-0d00-0000-6225074a0000"' + expires: + - '-1' + location: + - https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + pragma: + - no-cache + request-context: + - appId=cid-v1:54fcb76e-7bac-4b3e-96f8-8868676d3826 + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + x-ms-providerhub-traffic: + - 'True' + x-ms-ratelimit-remaining-subscription-deletes: + - '14999' + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Deleting","startTime":"2022-03-06T19:11:05.8767667Z","error":{}}' + headers: + cache-control: + - no-cache + content-length: + - '515' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:11:36 GMT + etag: + - '"8200f06d-0000-0d00-0000-6225074b0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Deleting","startTime":"2022-03-06T19:11:05.8767667Z","error":{}}' + headers: + cache-control: + - no-cache + content-length: + - '515' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:12:06 GMT + etag: + - '"8200f06d-0000-0d00-0000-6225074b0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Deleting","startTime":"2022-03-06T19:11:05.8767667Z","error":{}}' + headers: + cache-control: + - no-cache + content-length: + - '515' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:12:36 GMT + etag: + - '"8200f06d-0000-0d00-0000-6225074b0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Deleting","startTime":"2022-03-06T19:11:05.8767667Z","error":{}}' + headers: + cache-control: + - no-cache + content-length: + - '515' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:13:06 GMT + etag: + - '"8200f06d-0000-0d00-0000-6225074b0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Deleting","startTime":"2022-03-06T19:11:05.8767667Z","error":{}}' + headers: + cache-control: + - no-cache + content-length: + - '515' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:13:37 GMT + etag: + - '"8200f06d-0000-0d00-0000-6225074b0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Deleting","startTime":"2022-03-06T19:11:05.8767667Z","error":{}}' + headers: + cache-control: + - no-cache + content-length: + - '515' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:14:07 GMT + etag: + - '"8200f06d-0000-0d00-0000-6225074b0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Deleting","startTime":"2022-03-06T19:11:05.8767667Z","error":{}}' + headers: + cache-control: + - no-cache + content-length: + - '515' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:14:37 GMT + etag: + - '"8200f06d-0000-0d00-0000-6225074b0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Deleting","startTime":"2022-03-06T19:11:05.8767667Z","error":{}}' + headers: + cache-control: + - no-cache + content-length: + - '515' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:15:07 GMT + etag: + - '"8200f06d-0000-0d00-0000-6225074b0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Deleting","startTime":"2022-03-06T19:11:05.8767667Z","error":{}}' + headers: + cache-control: + - no-cache + content-length: + - '515' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:15:37 GMT + etag: + - '"8200f06d-0000-0d00-0000-6225074b0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Deleting","startTime":"2022-03-06T19:11:05.8767667Z","error":{}}' + headers: + cache-control: + - no-cache + content-length: + - '515' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:16:07 GMT + etag: + - '"8200f06d-0000-0d00-0000-6225074b0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + x-content-type-options: + - nosniff + status: + code: 202 + message: Accepted +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana delete + Connection: + - keep-alive + ParameterSetName: + - -g -n --yes + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-azure-mgmt-resource/20.0.0 Python/3.8.8 + (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97?api-version=2021-09-01-preview + response: + body: + string: '{"id":"/providers/Microsoft.Dashboard/locations/WESTEUROPE/operationStatuses/dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","name":"dc1f4407-4966-48c7-9ceb-425b17116e61*C607E3A5F1C17CECD2978FCB885153A785E95679378145314D9E4E43EAE14F97","resourceId":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/cli_test_amg000001/providers/Microsoft.Dashboard/grafana/clitestamg","status":"Succeeded","startTime":"2022-03-06T19:11:05.8767667Z","endTime":"2022-03-06T19:16:26.3790885Z","error":{},"properties":null}' + headers: + cache-control: + - no-cache + content-length: + - '575' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:16:37 GMT + etag: + - '"82002d71-0000-0d00-0000-6225088a0000"' + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + transfer-encoding: + - chunked + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + CommandName: + - grafana list + Connection: + - keep-alive + User-Agent: + - AZURECLI/2.33.0 (MSI) azsdk-python-mgmt-dashboard/1.0.0b1 Python/3.8.8 (Windows-10-10.0.22000-SP0) + method: GET + uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Dashboard/grafana?api-version=2021-09-01-preview + response: + body: + string: '{"value":[{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/yugangw/providers/Microsoft.Dashboard/grafana/yugangweus2e","name":"yugangweus2e","type":"microsoft.dashboard/grafana","sku":{"name":"Standard"},"location":"eastus2euap","tags":{},"systemData":{"createdBy":"yugangw@microsoft.com","createdByType":"User","createdAt":"2022-03-04T03:45:10.1825533Z","lastModifiedBy":"yugangw@microsoft.com","lastModifiedByType":"User","lastModifiedAt":"2022-03-04T03:45:10.1825533Z"},"identity":{"principalId":"f462ffcd-951a-4521-82b0-0cff25c5c18a","tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47","type":"SystemAssigned"},"properties":{"provisioningState":"Succeeded","grafanaVersion":"8.4.1","endpoint":"https://yugangweus2e.eus2e.azgrafana.io","zoneRedundancy":"Disabled"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/yugangw/providers/Microsoft.Dashboard/grafana/yugangwcus3","name":"yugangwcus3","type":"microsoft.dashboard/grafana","sku":{"name":"Standard"},"location":"centraluseuap","tags":{},"systemData":{"createdBy":"yugangw@microsoft.com","createdByType":"User","createdAt":"2022-02-26T04:13:32.6306926Z","lastModifiedBy":"ce34e7e5-485f-4d76-964f-b3d2b16d1e4f","lastModifiedByType":"Application","lastModifiedAt":"2022-02-26T04:17:52.2265322Z"},"identity":{"principalId":"a2bd0803-4837-42e5-a1b8-d63bfa4f6a90","tenantId":"72f988bf-86f1-41af-91ab-2d7cd011db47","type":"SystemAssigned"},"properties":{"provisioningState":"Succeeded","grafanaVersion":"8.4.1","endpoint":"https://yugangwcus3.cuse.azgrafana.io","zoneRedundancy":"Disabled"}},{"id":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/yugangw/providers/Microsoft.Dashboard/grafana/yugangweus","name":"yugangweus","type":"microsoft.dashboard/grafana","sku":{"name":"Standard"},"location":"eastus","tags":{"doo":"foo"},"systemData":{"createdBy":"yugangw@microsoft.com","createdByType":"User","createdAt":"2022-03-06T18:29:10.4356225Z","lastModifiedBy":"yugangw@microsoft.com","lastModifiedByType":"User","lastModifiedAt":"2022-03-06T18:29:10.4356225Z"},"properties":{"provisioningState":"Succeeded","grafanaVersion":"8.4.1","endpoint":"https://yugangweus.eus.azgrafana.io","zoneRedundancy":"Disabled"}}]}' + headers: + cache-control: + - no-cache + content-length: + - '2235' + content-type: + - application/json; charset=utf-8 + date: + - Sun, 06 Mar 2022 19:16:39 GMT + expires: + - '-1' + pragma: + - no-cache + strict-transport-security: + - max-age=31536000; includeSubDomains + vary: + - Accept-Encoding + x-content-type-options: + - nosniff + x-ms-original-request-ids: + - 391f4478-64fa-4311-8704-0350b9e18e2b + - ab52290e-ac8b-4be7-b377-7eda188afe4a + - 0d2d4deb-715c-45ca-9bc6-15be1e190b58 + - 31a5c63b-0536-484a-8dcc-1ce72fc82ca6 + - 80b9ac79-b98c-42d1-bba1-ba719e3c07af + status: + code: 200 + message: OK +version: 1 diff --git a/src/amg/azext_amg/tests/latest/test_amg_scenario.py b/src/amg/azext_amg/tests/latest/test_amg_scenario.py new file mode 100644 index 00000000000..bf9f603f27e --- /dev/null +++ b/src/amg/azext_amg/tests/latest/test_amg_scenario.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. +# -------------------------------------------------------------------------------------------- + +import os +import unittest + +from azure.cli.testsdk import (ScenarioTest, ResourceGroupPreparer) + + +TEST_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), '..')) + + +class AgsScenarioTest(ScenarioTest): + + @ResourceGroupPreparer(name_prefix='cli_test_amg') + def test_amg_e2e(self, resource_group): + + self.kwargs.update({ + 'name': 'clitestamg', + 'location': 'westeurope' + }) + + self.cmd('grafana create -g {rg} -n {name} -l {location} --tags foo=doo --skip-role-assignments', checks=[ + self.check('tags.foo', 'doo'), + self.check('name', '{name}') + ]) + self.cmd('grafana list -g {rg}') + count = len(self.cmd('grafana list').get_output_in_json()) + self.cmd('grafana show -g {rg} -n {name}', checks=[ + self.check('name', '{name}'), + self.check('resourceGroup', '{rg}'), + self.check('tags.foo', 'doo') + ]) + + self.cmd('grafana delete -g {rg} -n {name} --yes') + final_count = len(self.cmd('grafana list').get_output_in_json()) + self.assertTrue(final_count, count - 1) \ No newline at end of file diff --git a/src/amg/azext_amg/vendored_sdks/__init__.py b/src/amg/azext_amg/vendored_sdks/__init__.py new file mode 100644 index 00000000000..df8a6cb70ab --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/__init__.py @@ -0,0 +1,18 @@ +# 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 ._dashboard_management_client import DashboardManagementClient +from ._version import VERSION + +__version__ = VERSION +__all__ = ['DashboardManagementClient'] + +# `._patch.py` is used for handwritten extensions to the generated code +# Example: https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/customize_code/how-to-patch-sdk-code.md +from ._patch import patch_sdk +patch_sdk() diff --git a/src/amg/azext_amg/vendored_sdks/_configuration.py b/src/amg/azext_amg/vendored_sdks/_configuration.py new file mode 100644 index 00000000000..08abd2ffed1 --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/_configuration.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, TYPE_CHECKING + +from azure.core.configuration import Configuration +from azure.core.pipeline import policies +from azure.mgmt.core.policies import ARMChallengeAuthenticationPolicy, ARMHttpLoggingPolicy + +from ._version import VERSION + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from azure.core.credentials import TokenCredential + + +class DashboardManagementClientConfiguration(Configuration): + """Configuration for DashboardManagementClient. + + 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: Gets subscription credentials which uniquely identify Microsoft Azure subscription. The subscription ID forms part of the URI for every service call. + :type subscription_id: str + """ + + def __init__( + self, + credential: "TokenCredential", + subscription_id: str, + **kwargs: Any + ) -> None: + super(DashboardManagementClientConfiguration, self).__init__(**kwargs) + 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.") + + self.credential = credential + self.subscription_id = subscription_id + self.api_version = "2021-09-01-preview" + self.credential_scopes = kwargs.pop('credential_scopes', ['https://management.azure.com/.default']) + kwargs.setdefault('sdk_moniker', 'mgmt-dashboard/{}'.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 = ARMChallengeAuthenticationPolicy(self.credential, *self.credential_scopes, **kwargs) diff --git a/src/amg/azext_amg/vendored_sdks/_dashboard_management_client.py b/src/amg/azext_amg/vendored_sdks/_dashboard_management_client.py new file mode 100644 index 00000000000..4401d7f476b --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/_dashboard_management_client.py @@ -0,0 +1,97 @@ +# 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 copy import deepcopy +from typing import Any, Optional, TYPE_CHECKING + +from azure.core.rest import HttpRequest, HttpResponse +from azure.mgmt.core import ARMPipelineClient +from msrest import Deserializer, Serializer + +from . import models +from ._configuration import DashboardManagementClientConfiguration +from .operations import GrafanaOperations, Operations + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from azure.core.credentials import TokenCredential + +class DashboardManagementClient: + """The Microsoft.Dashboard Rest API spec. + + :ivar operations: Operations operations + :vartype operations: dashboard_management_client.operations.Operations + :ivar grafana: GrafanaOperations operations + :vartype grafana: dashboard_management_client.operations.GrafanaOperations + :param credential: Credential needed for the client to connect to Azure. + :type credential: ~azure.core.credentials.TokenCredential + :param subscription_id: Gets subscription credentials which uniquely identify Microsoft Azure + subscription. The subscription ID forms part of the URI for every service call. + :type subscription_id: str + :param base_url: Service URL. Default value is ''. + :type base_url: str + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no + Retry-After header is present. + """ + + def __init__( + self, + credential: "TokenCredential", + subscription_id: str, + base_url: str = "", + **kwargs: Any + ) -> None: + self._config = DashboardManagementClientConfiguration(credential=credential, subscription_id=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._serialize.client_side_validation = False + self.operations = Operations(self._client, self._config, self._serialize, self._deserialize) + self.grafana = GrafanaOperations(self._client, self._config, self._serialize, self._deserialize) + + + def _send_request( + self, + request, # type: HttpRequest + **kwargs: Any + ) -> HttpResponse: + """Runs the network request through the client's chained policies. + + >>> from azure.core.rest import HttpRequest + >>> request = HttpRequest("GET", "https://www.example.org/") + + >>> response = client._send_request(request) + + + For more information on this code flow, see https://aka.ms/azsdk/python/protocol/quickstart + + :param request: The network request you want to make. Required. + :type request: ~azure.core.rest.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to False. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.rest.HttpResponse + """ + + request_copy = deepcopy(request) + request_copy.url = self._client.format_url(request_copy.url) + return self._client.send_request(request_copy, **kwargs) + + def close(self): + # type: () -> None + self._client.close() + + def __enter__(self): + # type: () -> DashboardManagementClient + self._client.__enter__() + return self + + def __exit__(self, *exc_details): + # type: (Any) -> None + self._client.__exit__(*exc_details) diff --git a/src/amg/azext_amg/vendored_sdks/_patch.py b/src/amg/azext_amg/vendored_sdks/_patch.py new file mode 100644 index 00000000000..74e48ecd07c --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/_patch.py @@ -0,0 +1,31 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- + +# This file is used for handwritten extensions to the generated code. Example: +# https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/customize_code/how-to-patch-sdk-code.md +def patch_sdk(): + pass \ No newline at end of file diff --git a/src/amg/azext_amg/vendored_sdks/_vendor.py b/src/amg/azext_amg/vendored_sdks/_vendor.py new file mode 100644 index 00000000000..138f663c53a --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/_vendor.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. +# 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.pipeline.transport import HttpRequest + +def _convert_request(request, files=None): + data = request.content if not files else None + request = HttpRequest(method=request.method, url=request.url, headers=request.headers, data=data) + if files: + request.set_formdata_body(files) + return request + +def _format_url_section(template, **kwargs): + components = template.split("/") + while components: + try: + return template.format(**kwargs) + except KeyError as key: + formatted_components = template.split("/") + components = [ + c for c in formatted_components if "{}".format(key.args[0]) not in c + ] + template = "/".join(components) diff --git a/src/amg/azext_amg/vendored_sdks/_version.py b/src/amg/azext_amg/vendored_sdks/_version.py new file mode 100644 index 00000000000..e5754a47ce6 --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/_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 = "1.0.0b1" diff --git a/src/amg/azext_amg/vendored_sdks/aio/__init__.py b/src/amg/azext_amg/vendored_sdks/aio/__init__.py new file mode 100644 index 00000000000..30cf3e032d9 --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/aio/__init__.py @@ -0,0 +1,15 @@ +# 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 ._dashboard_management_client import DashboardManagementClient +__all__ = ['DashboardManagementClient'] + +# `._patch.py` is used for handwritten extensions to the generated code +# Example: https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/customize_code/how-to-patch-sdk-code.md +from ._patch import patch_sdk +patch_sdk() diff --git a/src/amg/azext_amg/vendored_sdks/aio/_configuration.py b/src/amg/azext_amg/vendored_sdks/aio/_configuration.py new file mode 100644 index 00000000000..c8c1228bcc4 --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/aio/_configuration.py @@ -0,0 +1,67 @@ +# 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, AsyncARMChallengeAuthenticationPolicy + +from .._version import VERSION + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from azure.core.credentials_async import AsyncTokenCredential + + +class DashboardManagementClientConfiguration(Configuration): + """Configuration for DashboardManagementClient. + + 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: Gets subscription credentials which uniquely identify Microsoft Azure subscription. The subscription ID forms part of the URI for every service call. + :type subscription_id: str + """ + + def __init__( + self, + credential: "AsyncTokenCredential", + subscription_id: str, + **kwargs: Any + ) -> None: + super(DashboardManagementClientConfiguration, self).__init__(**kwargs) + 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.") + + self.credential = credential + self.subscription_id = subscription_id + self.api_version = "2021-09-01-preview" + self.credential_scopes = kwargs.pop('credential_scopes', ['https://management.azure.com/.default']) + kwargs.setdefault('sdk_moniker', 'mgmt-dashboard/{}'.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 = AsyncARMChallengeAuthenticationPolicy(self.credential, *self.credential_scopes, **kwargs) diff --git a/src/amg/azext_amg/vendored_sdks/aio/_dashboard_management_client.py b/src/amg/azext_amg/vendored_sdks/aio/_dashboard_management_client.py new file mode 100644 index 00000000000..63d918012b1 --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/aio/_dashboard_management_client.py @@ -0,0 +1,94 @@ +# 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 copy import deepcopy +from typing import Any, Awaitable, Optional, TYPE_CHECKING + +from azure.core.rest import AsyncHttpResponse, HttpRequest +from azure.mgmt.core import AsyncARMPipelineClient +from msrest import Deserializer, Serializer + +from .. import models +from ._configuration import DashboardManagementClientConfiguration +from .operations import GrafanaOperations, Operations + +if TYPE_CHECKING: + # pylint: disable=unused-import,ungrouped-imports + from azure.core.credentials_async import AsyncTokenCredential + +class DashboardManagementClient: + """The Microsoft.Dashboard Rest API spec. + + :ivar operations: Operations operations + :vartype operations: dashboard_management_client.aio.operations.Operations + :ivar grafana: GrafanaOperations operations + :vartype grafana: dashboard_management_client.aio.operations.GrafanaOperations + :param credential: Credential needed for the client to connect to Azure. + :type credential: ~azure.core.credentials_async.AsyncTokenCredential + :param subscription_id: Gets subscription credentials which uniquely identify Microsoft Azure + subscription. The subscription ID forms part of the URI for every service call. + :type subscription_id: str + :param base_url: Service URL. Default value is ''. + :type base_url: str + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no + Retry-After header is present. + """ + + def __init__( + self, + credential: "AsyncTokenCredential", + subscription_id: str, + base_url: str = "", + **kwargs: Any + ) -> None: + self._config = DashboardManagementClientConfiguration(credential=credential, subscription_id=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._serialize.client_side_validation = False + self.operations = Operations(self._client, self._config, self._serialize, self._deserialize) + self.grafana = GrafanaOperations(self._client, self._config, self._serialize, self._deserialize) + + + def _send_request( + self, + request: HttpRequest, + **kwargs: Any + ) -> Awaitable[AsyncHttpResponse]: + """Runs the network request through the client's chained policies. + + >>> from azure.core.rest import HttpRequest + >>> request = HttpRequest("GET", "https://www.example.org/") + + >>> response = await client._send_request(request) + + + For more information on this code flow, see https://aka.ms/azsdk/python/protocol/quickstart + + :param request: The network request you want to make. Required. + :type request: ~azure.core.rest.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to False. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.rest.AsyncHttpResponse + """ + + request_copy = deepcopy(request) + request_copy.url = self._client.format_url(request_copy.url) + return self._client.send_request(request_copy, **kwargs) + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> "DashboardManagementClient": + await self._client.__aenter__() + return self + + async def __aexit__(self, *exc_details) -> None: + await self._client.__aexit__(*exc_details) diff --git a/src/amg/azext_amg/vendored_sdks/aio/_patch.py b/src/amg/azext_amg/vendored_sdks/aio/_patch.py new file mode 100644 index 00000000000..74e48ecd07c --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/aio/_patch.py @@ -0,0 +1,31 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# -------------------------------------------------------------------------- + +# This file is used for handwritten extensions to the generated code. Example: +# https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/customize_code/how-to-patch-sdk-code.md +def patch_sdk(): + pass \ No newline at end of file diff --git a/src/amg/azext_amg/vendored_sdks/aio/operations/__init__.py b/src/amg/azext_amg/vendored_sdks/aio/operations/__init__.py new file mode 100644 index 00000000000..81c85b3399f --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/aio/operations/__init__.py @@ -0,0 +1,15 @@ +# 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 ._operations import Operations +from ._grafana_operations import GrafanaOperations + +__all__ = [ + 'Operations', + 'GrafanaOperations', +] diff --git a/src/amg/azext_amg/vendored_sdks/aio/operations/_grafana_operations.py b/src/amg/azext_amg/vendored_sdks/aio/operations/_grafana_operations.py new file mode 100644 index 00000000000..e6f4512da87 --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/aio/operations/_grafana_operations.py @@ -0,0 +1,534 @@ +# 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 functools +from typing import Any, AsyncIterable, Callable, Dict, Generic, Optional, TypeVar, Union +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 +from azure.core.polling import AsyncLROPoller, AsyncNoPolling, AsyncPollingMethod +from azure.core.rest import HttpRequest +from azure.core.tracing.decorator import distributed_trace +from azure.core.tracing.decorator_async import distributed_trace_async +from azure.mgmt.core.exceptions import ARMErrorFormat +from azure.mgmt.core.polling.async_arm_polling import AsyncARMPolling + +from ... import models as _models +from ..._vendor import _convert_request +from ...operations._grafana_operations import build_create_request_initial, build_delete_request_initial, build_get_request, build_list_by_resource_group_request, build_list_request, build_update_request +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, Dict[str, Any]], Any]] + +class GrafanaOperations: + """GrafanaOperations 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: ~dashboard_management_client.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 + + @distributed_trace + def list( + self, + **kwargs: Any + ) -> AsyncIterable["_models.GrafanaResourceListResponse"]: + """List all resources of workspaces for Grafana under the specified subscription. + + List all resources of workspaces for Grafana under the specified subscription. + + :keyword callable cls: A custom type or function that will be passed the direct response + :return: An iterator like instance of either GrafanaResourceListResponse or the result of + cls(response) + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~dashboard_management_client.models.GrafanaResourceListResponse] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResourceListResponse"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + def prepare_request(next_link=None): + if not next_link: + + request = build_list_request( + subscription_id=self._config.subscription_id, + template_url=self.list.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + else: + + request = build_list_request( + subscription_id=self._config.subscription_id, + template_url=next_link, + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + request.method = "GET" + return request + + async def extract_data(pipeline_response): + deserialized = self._deserialize("GrafanaResourceListResponse", 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]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + return pipeline_response + + + return AsyncItemPaged( + get_next, extract_data + ) + list.metadata = {'url': '/subscriptions/{subscriptionId}/providers/Microsoft.Dashboard/grafana'} # type: ignore + + @distributed_trace + def list_by_resource_group( + self, + resource_group_name: str, + **kwargs: Any + ) -> AsyncIterable["_models.GrafanaResourceListResponse"]: + """List all resources of workspaces for Grafana under the specified resource group. + + List all resources of workspaces for Grafana under the specified resource group. + + :param resource_group_name: The name of the resource group. The name is case insensitive. + :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 GrafanaResourceListResponse or the result of + cls(response) + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~dashboard_management_client.models.GrafanaResourceListResponse] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResourceListResponse"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + def prepare_request(next_link=None): + if not next_link: + + request = build_list_by_resource_group_request( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + template_url=self.list_by_resource_group.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + else: + + request = build_list_by_resource_group_request( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + template_url=next_link, + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + request.method = "GET" + return request + + async def extract_data(pipeline_response): + deserialized = self._deserialize("GrafanaResourceListResponse", 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]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + return pipeline_response + + + return AsyncItemPaged( + get_next, extract_data + ) + list_by_resource_group.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana'} # type: ignore + + @distributed_trace_async + async def get( + self, + resource_group_name: str, + workspace_name: str, + **kwargs: Any + ) -> "_models.GrafanaResource": + """Get the properties of a specific workspace for Grafana resource. + + Get the properties of a specific workspace for Grafana resource. + + :param resource_group_name: The name of the resource group. The name is case insensitive. + :type resource_group_name: str + :param workspace_name: The name of Azure Managed Workspace for Grafana. + :type workspace_name: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: GrafanaResource, or the result of cls(response) + :rtype: ~dashboard_management_client.models.GrafanaResource + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + + request = build_get_request( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + workspace_name=workspace_name, + template_url=self.get.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + 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.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('GrafanaResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + + get.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore + + + async def _create_initial( + self, + resource_group_name: str, + workspace_name: str, + body: Optional["_models.GrafanaResource"] = None, + **kwargs: Any + ) -> "_models.GrafanaResource": + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + content_type = kwargs.pop('content_type', "application/json") # type: Optional[str] + + if body is not None: + _json = self._serialize.body(body, 'GrafanaResource') + else: + _json = None + + request = build_create_request_initial( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + workspace_name=workspace_name, + content_type=content_type, + json=_json, + template_url=self._create_initial.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200, 201]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response, error_format=ARMErrorFormat) + + if response.status_code == 200: + deserialized = self._deserialize('GrafanaResource', pipeline_response) + + if response.status_code == 201: + deserialized = self._deserialize('GrafanaResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + + _create_initial.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore + + + @distributed_trace_async + async def begin_create( + self, + resource_group_name: str, + workspace_name: str, + body: Optional["_models.GrafanaResource"] = None, + **kwargs: Any + ) -> AsyncLROPoller["_models.GrafanaResource"]: + """Create or update a workspace for Grafana resource. This API is idempotent, so user can either + create a new grafana or update an existing grafana. + + Create or update a workspace for Grafana resource. This API is idempotent, so user can either + create a new grafana or update an existing grafana. + + :param resource_group_name: The name of the resource group. The name is case insensitive. + :type resource_group_name: str + :param workspace_name: The name of Azure Managed Workspace for Grafana. + :type workspace_name: str + :param body: + :type body: ~dashboard_management_client.models.GrafanaResource + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be AsyncARMPolling. Pass in False for + this operation to not poll, or pass in your own initialized polling object for a personal + polling strategy. + :paramtype polling: bool or ~azure.core.polling.AsyncPollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no + Retry-After header is present. + :return: An instance of AsyncLROPoller that returns either GrafanaResource or the result of + cls(response) + :rtype: ~azure.core.polling.AsyncLROPoller[~dashboard_management_client.models.GrafanaResource] + :raises: ~azure.core.exceptions.HttpResponseError + """ + content_type = kwargs.pop('content_type', "application/json") # type: Optional[str] + polling = kwargs.pop('polling', True) # type: Union[bool, azure.core.polling.AsyncPollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResource"] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = await self._create_initial( + resource_group_name=resource_group_name, + workspace_name=workspace_name, + body=body, + content_type=content_type, + cls=lambda x,y,z: x, + **kwargs + ) + kwargs.pop('error_map', None) + + def get_long_running_output(pipeline_response): + response = pipeline_response.http_response + deserialized = self._deserialize('GrafanaResource', pipeline_response) + if cls: + return cls(pipeline_response, deserialized, {}) + return deserialized + + + if polling is True: polling_method = AsyncARMPolling(lro_delay, lro_options={'final-state-via': 'azure-async-operation'}, **kwargs) + elif polling is False: polling_method = AsyncNoPolling() + else: polling_method = polling + if cont_token: + return AsyncLROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return AsyncLROPoller(self._client, raw_result, get_long_running_output, polling_method) + + begin_create.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore + + @distributed_trace_async + async def update( + self, + resource_group_name: str, + workspace_name: str, + body: Optional["_models.GrafanaResourceUpdateParameters"] = None, + **kwargs: Any + ) -> "_models.GrafanaResource": + """Update a workspace for Grafana resource. + + Update a workspace for Grafana resource. + + :param resource_group_name: The name of the resource group. The name is case insensitive. + :type resource_group_name: str + :param workspace_name: The name of Azure Managed Workspace for Grafana. + :type workspace_name: str + :param body: + :type body: ~dashboard_management_client.models.GrafanaResourceUpdateParameters + :keyword callable cls: A custom type or function that will be passed the direct response + :return: GrafanaResource, or the result of cls(response) + :rtype: ~dashboard_management_client.models.GrafanaResource + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + content_type = kwargs.pop('content_type', "application/json") # type: Optional[str] + + if body is not None: + _json = self._serialize.body(body, 'GrafanaResourceUpdateParameters') + else: + _json = None + + request = build_update_request( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + workspace_name=workspace_name, + content_type=content_type, + json=_json, + template_url=self.update.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + 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.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('GrafanaResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + + update.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore + + + async def _delete_initial( + self, + resource_group_name: str, + workspace_name: str, + **kwargs: Any + ) -> None: + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + + request = build_delete_request_initial( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + workspace_name=workspace_name, + template_url=self._delete_initial.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + pipeline_response = await self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200, 202, 204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response, error_format=ARMErrorFormat) + + if cls: + return cls(pipeline_response, None, {}) + + _delete_initial.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore + + + @distributed_trace_async + async def begin_delete( + self, + resource_group_name: str, + workspace_name: str, + **kwargs: Any + ) -> AsyncLROPoller[None]: + """Delete a workspace for Grafana resource. + + Delete a workspace for Grafana resource. + + :param resource_group_name: The name of the resource group. The name is case insensitive. + :type resource_group_name: str + :param workspace_name: The name of Azure Managed Workspace for Grafana. + :type workspace_name: str + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be AsyncARMPolling. Pass in False for + this operation to not poll, or pass in your own initialized polling object for a personal + polling strategy. + :paramtype polling: bool or ~azure.core.polling.AsyncPollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no + Retry-After header is present. + :return: An instance of AsyncLROPoller that returns either None or the result of cls(response) + :rtype: ~azure.core.polling.AsyncLROPoller[None] + :raises: ~azure.core.exceptions.HttpResponseError + """ + polling = kwargs.pop('polling', True) # type: Union[bool, azure.core.polling.AsyncPollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType[None] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = await self._delete_initial( + resource_group_name=resource_group_name, + workspace_name=workspace_name, + cls=lambda x,y,z: x, + **kwargs + ) + kwargs.pop('error_map', None) + + def get_long_running_output(pipeline_response): + if cls: + return cls(pipeline_response, None, {}) + + + if polling is True: polling_method = AsyncARMPolling(lro_delay, lro_options={'final-state-via': 'azure-async-operation'}, **kwargs) + elif polling is False: polling_method = AsyncNoPolling() + else: polling_method = polling + if cont_token: + return AsyncLROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return AsyncLROPoller(self._client, raw_result, get_long_running_output, polling_method) + + begin_delete.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore diff --git a/src/amg/azext_amg/vendored_sdks/aio/operations/_operations.py b/src/amg/azext_amg/vendored_sdks/aio/operations/_operations.py new file mode 100644 index 00000000000..91559e425d8 --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/aio/operations/_operations.py @@ -0,0 +1,112 @@ +# 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 functools +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 +from azure.core.rest import HttpRequest +from azure.core.tracing.decorator import distributed_trace +from azure.core.tracing.decorator_async import distributed_trace_async +from azure.mgmt.core.exceptions import ARMErrorFormat + +from ... import models as _models +from ..._vendor import _convert_request +from ...operations._operations import build_list_request +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: ~dashboard_management_client.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 + + @distributed_trace + def list( + self, + **kwargs: Any + ) -> AsyncIterable["_models.OperationListResult"]: + """List all available API operations provided by Microsoft.Dashboard. + + List all available API operations provided by Microsoft.Dashboard. + + :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[~dashboard_management_client.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', {})) + def prepare_request(next_link=None): + if not next_link: + + request = build_list_request( + template_url=self.list.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + else: + + request = build_list_request( + template_url=next_link, + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + request.method = "GET" + 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 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]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + return pipeline_response + + + return AsyncItemPaged( + get_next, extract_data + ) + list.metadata = {'url': '/providers/Microsoft.Dashboard/operations'} # type: ignore diff --git a/src/amg/azext_amg/vendored_sdks/models/__init__.py b/src/amg/azext_amg/vendored_sdks/models/__init__.py new file mode 100644 index 00000000000..f79c863e098 --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/models/__init__.py @@ -0,0 +1,57 @@ +# 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 ._models_py3 import ErrorAdditionalInfo +from ._models_py3 import ErrorDetail +from ._models_py3 import ErrorResponse +from ._models_py3 import GrafanaResource +from ._models_py3 import GrafanaResourceListResponse +from ._models_py3 import GrafanaResourceProperties +from ._models_py3 import GrafanaResourceUpdateParameters +from ._models_py3 import ManagedIdentity +from ._models_py3 import OperationDisplay +from ._models_py3 import OperationListResult +from ._models_py3 import OperationResult +from ._models_py3 import ResourceSku +from ._models_py3 import SystemData +from ._models_py3 import UserAssignedIdentity + + +from ._dashboard_management_client_enums import ( + ActionType, + CreatedByType, + IdentityType, + LastModifiedByType, + Origin, + ProvisioningState, + ZoneRedundancy, +) + +__all__ = [ + 'ErrorAdditionalInfo', + 'ErrorDetail', + 'ErrorResponse', + 'GrafanaResource', + 'GrafanaResourceListResponse', + 'GrafanaResourceProperties', + 'GrafanaResourceUpdateParameters', + 'ManagedIdentity', + 'OperationDisplay', + 'OperationListResult', + 'OperationResult', + 'ResourceSku', + 'SystemData', + 'UserAssignedIdentity', + 'ActionType', + 'CreatedByType', + 'IdentityType', + 'LastModifiedByType', + 'Origin', + 'ProvisioningState', + 'ZoneRedundancy', +] diff --git a/src/amg/azext_amg/vendored_sdks/models/_dashboard_management_client_enums.py b/src/amg/azext_amg/vendored_sdks/models/_dashboard_management_client_enums.py new file mode 100644 index 00000000000..bd3a3641b2d --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/models/_dashboard_management_client_enums.py @@ -0,0 +1,67 @@ +# 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 +from six import with_metaclass +from azure.core import CaseInsensitiveEnumMeta + + +class ActionType(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + """Indicates the action type. "Internal" refers to actions that are for internal only APIs. + """ + + INTERNAL = "Internal" + +class CreatedByType(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + """The type of identity that created the resource. + """ + + USER = "User" + APPLICATION = "Application" + MANAGED_IDENTITY = "ManagedIdentity" + KEY = "Key" + +class IdentityType(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + """The type 'SystemAssigned, UserAssigned' includes both an implicitly created identity and a set + of user assigned identities. The type 'None' will remove any identities from the resource. + """ + + NONE = "None" + SYSTEM_ASSIGNED = "SystemAssigned" + +class LastModifiedByType(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + + USER = "User" + APPLICATION = "Application" + MANAGED_IDENTITY = "ManagedIdentity" + KEY = "Key" + +class Origin(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + """The intended executor of the operation. + """ + + USER = "user" + SYSTEM = "system" + USER_SYSTEM = "user,system" + +class ProvisioningState(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + + ACCEPTED = "Accepted" + CREATING = "Creating" + UPDATING = "Updating" + DELETING = "Deleting" + SUCCEEDED = "Succeeded" + FAILED = "Failed" + CANCELED = "Canceled" + DELETED = "Deleted" + NOT_SPECIFIED = "NotSpecified" + +class ZoneRedundancy(with_metaclass(CaseInsensitiveEnumMeta, str, Enum)): + + DISABLED = "Disabled" + ENABLED = "Enabled" diff --git a/src/amg/azext_amg/vendored_sdks/models/_models_py3.py b/src/amg/azext_amg/vendored_sdks/models/_models_py3.py new file mode 100644 index 00000000000..0b72d973be7 --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/models/_models_py3.py @@ -0,0 +1,623 @@ +# 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 ._dashboard_management_client_enums import * + + +class ErrorAdditionalInfo(msrest.serialization.Model): + """The resource management error additional info. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar type: The additional info type. + :vartype type: str + :ivar info: The additional info. + :vartype info: any + """ + + _validation = { + 'type': {'readonly': True}, + 'info': {'readonly': True}, + } + + _attribute_map = { + 'type': {'key': 'type', 'type': 'str'}, + 'info': {'key': 'info', 'type': 'object'}, + } + + def __init__( + self, + **kwargs + ): + """ + """ + super(ErrorAdditionalInfo, self).__init__(**kwargs) + self.type = None + self.info = None + + +class ErrorDetail(msrest.serialization.Model): + """The error detail. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar code: The error code. + :vartype code: str + :ivar message: The error message. + :vartype message: str + :ivar target: The error target. + :vartype target: str + :ivar details: The error details. + :vartype details: list[~dashboard_management_client.models.ErrorDetail] + :ivar additional_info: The error additional info. + :vartype additional_info: list[~dashboard_management_client.models.ErrorAdditionalInfo] + """ + + _validation = { + 'code': {'readonly': True}, + 'message': {'readonly': True}, + 'target': {'readonly': True}, + 'details': {'readonly': True}, + 'additional_info': {'readonly': True}, + } + + _attribute_map = { + 'code': {'key': 'code', 'type': 'str'}, + 'message': {'key': 'message', 'type': 'str'}, + 'target': {'key': 'target', 'type': 'str'}, + 'details': {'key': 'details', 'type': '[ErrorDetail]'}, + 'additional_info': {'key': 'additionalInfo', 'type': '[ErrorAdditionalInfo]'}, + } + + def __init__( + self, + **kwargs + ): + """ + """ + super(ErrorDetail, self).__init__(**kwargs) + self.code = None + self.message = None + self.target = None + self.details = None + self.additional_info = None + + +class ErrorResponse(msrest.serialization.Model): + """Common error response for all Azure Resource Manager APIs to return error details for failed operations. (This also follows the OData error response format.). + + :ivar error: The error object. + :vartype error: ~dashboard_management_client.models.ErrorDetail + """ + + _attribute_map = { + 'error': {'key': 'error', 'type': 'ErrorDetail'}, + } + + def __init__( + self, + *, + error: Optional["ErrorDetail"] = None, + **kwargs + ): + """ + :keyword error: The error object. + :paramtype error: ~dashboard_management_client.models.ErrorDetail + """ + super(ErrorResponse, self).__init__(**kwargs) + self.error = error + + +class GrafanaResource(msrest.serialization.Model): + """The grafana resource type. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar id: ARM id of the grafana resource. + :vartype id: str + :ivar name: Name of the grafana resource. + :vartype name: str + :ivar type: The type of the grafana resource. + :vartype type: str + :ivar sku: The Sku of the grafana resource. + :vartype sku: ~dashboard_management_client.models.ResourceSku + :ivar properties: Properties specific to the grafana resource. + :vartype properties: ~dashboard_management_client.models.GrafanaResourceProperties + :ivar identity: The managed identity of the grafana resource. + :vartype identity: ~dashboard_management_client.models.ManagedIdentity + :ivar system_data: The system meta data relating to this grafana resource. + :vartype system_data: ~dashboard_management_client.models.SystemData + :ivar tags: A set of tags. The tags for grafana resource. + :vartype tags: dict[str, str] + :ivar location: The geo-location where the grafana resource lives. + :vartype location: str + """ + + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True}, + 'type': {'readonly': True}, + 'system_data': {'readonly': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + 'sku': {'key': 'sku', 'type': 'ResourceSku'}, + 'properties': {'key': 'properties', 'type': 'GrafanaResourceProperties'}, + 'identity': {'key': 'identity', 'type': 'ManagedIdentity'}, + 'system_data': {'key': 'systemData', 'type': 'SystemData'}, + 'tags': {'key': 'tags', 'type': '{str}'}, + 'location': {'key': 'location', 'type': 'str'}, + } + + def __init__( + self, + *, + sku: Optional["ResourceSku"] = None, + properties: Optional["GrafanaResourceProperties"] = None, + identity: Optional["ManagedIdentity"] = None, + tags: Optional[Dict[str, str]] = None, + location: Optional[str] = None, + **kwargs + ): + """ + :keyword sku: The Sku of the grafana resource. + :paramtype sku: ~dashboard_management_client.models.ResourceSku + :keyword properties: Properties specific to the grafana resource. + :paramtype properties: ~dashboard_management_client.models.GrafanaResourceProperties + :keyword identity: The managed identity of the grafana resource. + :paramtype identity: ~dashboard_management_client.models.ManagedIdentity + :keyword tags: A set of tags. The tags for grafana resource. + :paramtype tags: dict[str, str] + :keyword location: The geo-location where the grafana resource lives. + :paramtype location: str + """ + super(GrafanaResource, self).__init__(**kwargs) + self.id = None + self.name = None + self.type = None + self.sku = sku + self.properties = properties + self.identity = identity + self.system_data = None + self.tags = tags + self.location = location + + +class GrafanaResourceListResponse(msrest.serialization.Model): + """GrafanaResourceListResponse. + + :ivar value: + :vartype value: list[~dashboard_management_client.models.GrafanaResource] + :ivar next_link: + :vartype next_link: str + """ + + _attribute_map = { + 'value': {'key': 'value', 'type': '[GrafanaResource]'}, + 'next_link': {'key': 'nextLink', 'type': 'str'}, + } + + def __init__( + self, + *, + value: Optional[List["GrafanaResource"]] = None, + next_link: Optional[str] = None, + **kwargs + ): + """ + :keyword value: + :paramtype value: list[~dashboard_management_client.models.GrafanaResource] + :keyword next_link: + :paramtype next_link: str + """ + super(GrafanaResourceListResponse, self).__init__(**kwargs) + self.value = value + self.next_link = next_link + + +class GrafanaResourceProperties(msrest.serialization.Model): + """Properties specific to the grafana resource. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar provisioning_state: Provisioning state of the resource. Possible values include: + "Accepted", "Creating", "Updating", "Deleting", "Succeeded", "Failed", "Canceled", "Deleted", + "NotSpecified". + :vartype provisioning_state: str or ~dashboard_management_client.models.ProvisioningState + :ivar grafana_version: The Grafana software version. + :vartype grafana_version: str + :ivar endpoint: The endpoint of the Grafana instance. + :vartype endpoint: str + :ivar zone_redundancy: Possible values include: "Disabled", "Enabled". Default value: + "Disabled". + :vartype zone_redundancy: str or ~dashboard_management_client.models.ZoneRedundancy + """ + + _validation = { + 'grafana_version': {'readonly': True}, + 'endpoint': {'readonly': True}, + } + + _attribute_map = { + 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, + 'grafana_version': {'key': 'grafanaVersion', 'type': 'str'}, + 'endpoint': {'key': 'endpoint', 'type': 'str'}, + 'zone_redundancy': {'key': 'zoneRedundancy', 'type': 'str'}, + } + + def __init__( + self, + *, + provisioning_state: Optional[Union[str, "ProvisioningState"]] = None, + zone_redundancy: Optional[Union[str, "ZoneRedundancy"]] = "Disabled", + **kwargs + ): + """ + :keyword provisioning_state: Provisioning state of the resource. Possible values include: + "Accepted", "Creating", "Updating", "Deleting", "Succeeded", "Failed", "Canceled", "Deleted", + "NotSpecified". + :paramtype provisioning_state: str or ~dashboard_management_client.models.ProvisioningState + :keyword zone_redundancy: Possible values include: "Disabled", "Enabled". Default value: + "Disabled". + :paramtype zone_redundancy: str or ~dashboard_management_client.models.ZoneRedundancy + """ + super(GrafanaResourceProperties, self).__init__(**kwargs) + self.provisioning_state = provisioning_state + self.grafana_version = None + self.endpoint = None + self.zone_redundancy = zone_redundancy + + +class GrafanaResourceUpdateParameters(msrest.serialization.Model): + """The parameters for a PATCH request to a grafana resource. + + :ivar identity: The managed identity of the grafana resource. + :vartype identity: ~dashboard_management_client.models.ManagedIdentity + :ivar tags: A set of tags. The new tags of the grafana resource. + :vartype tags: dict[str, str] + """ + + _attribute_map = { + 'identity': {'key': 'identity', 'type': 'ManagedIdentity'}, + 'tags': {'key': 'tags', 'type': '{str}'}, + } + + def __init__( + self, + *, + identity: Optional["ManagedIdentity"] = None, + tags: Optional[Dict[str, str]] = None, + **kwargs + ): + """ + :keyword identity: The managed identity of the grafana resource. + :paramtype identity: ~dashboard_management_client.models.ManagedIdentity + :keyword tags: A set of tags. The new tags of the grafana resource. + :paramtype tags: dict[str, str] + """ + super(GrafanaResourceUpdateParameters, self).__init__(**kwargs) + self.identity = identity + self.tags = tags + + +class ManagedIdentity(msrest.serialization.Model): + """The managed identity of a resource. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar type: The type 'SystemAssigned, UserAssigned' includes both an implicitly created + identity and a set of user assigned identities. The type 'None' will remove any identities from + the resource. Possible values include: "None", "SystemAssigned". + :vartype type: str or ~dashboard_management_client.models.IdentityType + :ivar principal_id: The principal id of the system assigned identity. + :vartype principal_id: str + :ivar tenant_id: The tenant id of the system assigned identity. + :vartype tenant_id: str + :ivar user_assigned_identities: Dictionary of user assigned identities. + :vartype user_assigned_identities: dict[str, + ~dashboard_management_client.models.UserAssignedIdentity] + """ + + _validation = { + 'principal_id': {'readonly': True}, + 'tenant_id': {'readonly': True}, + } + + _attribute_map = { + 'type': {'key': 'type', 'type': 'str'}, + 'principal_id': {'key': 'principalId', 'type': 'str'}, + 'tenant_id': {'key': 'tenantId', 'type': 'str'}, + 'user_assigned_identities': {'key': 'userAssignedIdentities', 'type': '{UserAssignedIdentity}'}, + } + + def __init__( + self, + *, + type: Optional[Union[str, "IdentityType"]] = None, + user_assigned_identities: Optional[Dict[str, "UserAssignedIdentity"]] = None, + **kwargs + ): + """ + :keyword type: The type 'SystemAssigned, UserAssigned' includes both an implicitly created + identity and a set of user assigned identities. The type 'None' will remove any identities from + the resource. Possible values include: "None", "SystemAssigned". + :paramtype type: str or ~dashboard_management_client.models.IdentityType + :keyword user_assigned_identities: Dictionary of user assigned identities. + :paramtype user_assigned_identities: dict[str, + ~dashboard_management_client.models.UserAssignedIdentity] + """ + super(ManagedIdentity, self).__init__(**kwargs) + self.type = type + self.principal_id = None + self.tenant_id = None + self.user_assigned_identities = user_assigned_identities + + +class OperationDisplay(msrest.serialization.Model): + """Localized display information for this particular operation. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar provider: The localized friendly form of the resource provider name, i.e., + Microsoft.Dashboard. + :vartype provider: str + :ivar resource: The localized friendly name of the resource type related to this operation, + e.g., 'grafana'. + :vartype resource: str + :ivar operation: Operation type, e.g., read, write, delete, etc. + :vartype operation: str + :ivar description: Description of the operation, e.g., 'Read grafana'. + :vartype description: str + """ + + _validation = { + 'provider': {'readonly': True}, + 'resource': {'readonly': True}, + 'operation': {'readonly': True}, + 'description': {'readonly': True}, + } + + _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 = None + self.resource = None + self.operation = None + self.description = None + + +class OperationListResult(msrest.serialization.Model): + """A list of REST API operations supported by Microsoft.Dashboard provider. It contains an URL link to get the next set of results. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar value: List of operations supported by the Microsoft.Dashboard provider. + :vartype value: list[~dashboard_management_client.models.OperationResult] + :ivar next_link: URL to get the next set of operation list results if there are any. + :vartype next_link: str + """ + + _validation = { + 'value': {'readonly': True}, + 'next_link': {'readonly': True}, + } + + _attribute_map = { + 'value': {'key': 'value', 'type': '[OperationResult]'}, + 'next_link': {'key': 'nextLink', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + """ + """ + super(OperationListResult, self).__init__(**kwargs) + self.value = None + self.next_link = None + + +class OperationResult(msrest.serialization.Model): + """A Microsoft.Dashboard REST API operation. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar name: Operation name, i.e., {provider}/{resource}/{operation}. + :vartype name: str + :ivar is_data_action: Indicates whether the operation applies to data-plane. Set "true" for + data-plane operations and "false" for ARM/control-plane operations. + :vartype is_data_action: bool + :ivar display: Localized display information for this particular operation. + :vartype display: ~dashboard_management_client.models.OperationDisplay + :ivar origin: The intended executor of the operation. Possible values include: "user", + "system", "user,system". + :vartype origin: str or ~dashboard_management_client.models.Origin + :ivar action_type: Indicates the action type. "Internal" refers to actions that are for + internal only APIs. Possible values include: "Internal". + :vartype action_type: str or ~dashboard_management_client.models.ActionType + """ + + _validation = { + 'name': {'readonly': True}, + 'is_data_action': {'readonly': True}, + 'origin': {'readonly': True}, + 'action_type': {'readonly': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + 'is_data_action': {'key': 'isDataAction', 'type': 'bool'}, + 'display': {'key': 'display', 'type': 'OperationDisplay'}, + 'origin': {'key': 'origin', 'type': 'str'}, + 'action_type': {'key': 'actionType', 'type': 'str'}, + } + + def __init__( + self, + *, + display: Optional["OperationDisplay"] = None, + **kwargs + ): + """ + :keyword display: Localized display information for this particular operation. + :paramtype display: ~dashboard_management_client.models.OperationDisplay + """ + super(OperationResult, self).__init__(**kwargs) + self.name = None + self.is_data_action = None + self.display = display + self.origin = None + self.action_type = None + + +class ResourceSku(msrest.serialization.Model): + """ResourceSku. + + All required parameters must be populated in order to send to Azure. + + :ivar name: Required. + :vartype name: str + """ + + _validation = { + 'name': {'required': True}, + } + + _attribute_map = { + 'name': {'key': 'name', 'type': 'str'}, + } + + def __init__( + self, + *, + name: str, + **kwargs + ): + """ + :keyword name: Required. + :paramtype name: str + """ + super(ResourceSku, self).__init__(**kwargs) + self.name = name + + +class SystemData(msrest.serialization.Model): + """SystemData. + + :ivar created_by: + :vartype created_by: str + :ivar created_by_type: The type of identity that created the resource. Possible values include: + "User", "Application", "ManagedIdentity", "Key". + :vartype created_by_type: str or ~dashboard_management_client.models.CreatedByType + :ivar created_at: + :vartype created_at: ~datetime.datetime + :ivar last_modified_by: + :vartype last_modified_by: str + :ivar last_modified_by_type: Possible values include: "User", "Application", "ManagedIdentity", + "Key". + :vartype last_modified_by_type: str or ~dashboard_management_client.models.LastModifiedByType + :ivar last_modified_at: + :vartype last_modified_at: ~datetime.datetime + """ + + _attribute_map = { + 'created_by': {'key': 'createdBy', 'type': 'str'}, + 'created_by_type': {'key': 'createdByType', 'type': 'str'}, + 'created_at': {'key': 'createdAt', 'type': 'iso-8601'}, + 'last_modified_by': {'key': 'lastModifiedBy', 'type': 'str'}, + 'last_modified_by_type': {'key': 'lastModifiedByType', 'type': 'str'}, + 'last_modified_at': {'key': 'lastModifiedAt', 'type': 'iso-8601'}, + } + + def __init__( + self, + *, + created_by: Optional[str] = None, + created_by_type: Optional[Union[str, "CreatedByType"]] = None, + created_at: Optional[datetime.datetime] = None, + last_modified_by: Optional[str] = None, + last_modified_by_type: Optional[Union[str, "LastModifiedByType"]] = None, + last_modified_at: Optional[datetime.datetime] = None, + **kwargs + ): + """ + :keyword created_by: + :paramtype created_by: str + :keyword created_by_type: The type of identity that created the resource. Possible values + include: "User", "Application", "ManagedIdentity", "Key". + :paramtype created_by_type: str or ~dashboard_management_client.models.CreatedByType + :keyword created_at: + :paramtype created_at: ~datetime.datetime + :keyword last_modified_by: + :paramtype last_modified_by: str + :keyword last_modified_by_type: Possible values include: "User", "Application", + "ManagedIdentity", "Key". + :paramtype last_modified_by_type: str or ~dashboard_management_client.models.LastModifiedByType + :keyword last_modified_at: + :paramtype last_modified_at: ~datetime.datetime + """ + super(SystemData, self).__init__(**kwargs) + self.created_by = created_by + self.created_by_type = created_by_type + self.created_at = created_at + self.last_modified_by = last_modified_by + self.last_modified_by_type = last_modified_by_type + self.last_modified_at = last_modified_at + + +class UserAssignedIdentity(msrest.serialization.Model): + """UserAssignedIdentity. + + Variables are only populated by the server, and will be ignored when sending a request. + + :ivar principal_id: The principal id of user assigned identity. + :vartype principal_id: str + :ivar client_id: The client id of user assigned identity. + :vartype client_id: str + """ + + _validation = { + 'principal_id': {'readonly': True}, + 'client_id': {'readonly': True}, + } + + _attribute_map = { + 'principal_id': {'key': 'principalId', 'type': 'str'}, + 'client_id': {'key': 'clientId', 'type': 'str'}, + } + + def __init__( + self, + **kwargs + ): + """ + """ + super(UserAssignedIdentity, self).__init__(**kwargs) + self.principal_id = None + self.client_id = None diff --git a/src/amg/azext_amg/vendored_sdks/operations/__init__.py b/src/amg/azext_amg/vendored_sdks/operations/__init__.py new file mode 100644 index 00000000000..81c85b3399f --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/operations/__init__.py @@ -0,0 +1,15 @@ +# 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 ._operations import Operations +from ._grafana_operations import GrafanaOperations + +__all__ = [ + 'Operations', + 'GrafanaOperations', +] diff --git a/src/amg/azext_amg/vendored_sdks/operations/_grafana_operations.py b/src/amg/azext_amg/vendored_sdks/operations/_grafana_operations.py new file mode 100644 index 00000000000..ca5fa25aaf3 --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/operations/_grafana_operations.py @@ -0,0 +1,758 @@ +# 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 functools +from typing import Any, Callable, Dict, Generic, Iterable, Optional, TypeVar, Union +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 HttpResponse +from azure.core.polling import LROPoller, NoPolling, PollingMethod +from azure.core.rest import HttpRequest +from azure.core.tracing.decorator import distributed_trace +from azure.mgmt.core.exceptions import ARMErrorFormat +from azure.mgmt.core.polling.arm_polling import ARMPolling +from msrest import Serializer + +from .. import models as _models +from .._vendor import _convert_request, _format_url_section +T = TypeVar('T') +JSONType = Any +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +_SERIALIZER = Serializer() +_SERIALIZER.client_side_validation = False + +def build_list_request( + subscription_id: str, + **kwargs: Any +) -> HttpRequest: + api_version = "2021-09-01-preview" + accept = "application/json" + # Construct URL + url = kwargs.pop("template_url", '/subscriptions/{subscriptionId}/providers/Microsoft.Dashboard/grafana') + path_format_arguments = { + "subscriptionId": _SERIALIZER.url("subscription_id", subscription_id, 'str', min_length=1), + } + + url = _format_url_section(url, **path_format_arguments) + + # Construct parameters + query_parameters = kwargs.pop("params", {}) # type: Dict[str, Any] + query_parameters['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = kwargs.pop("headers", {}) # type: Dict[str, Any] + header_parameters['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="GET", + url=url, + params=query_parameters, + headers=header_parameters, + **kwargs + ) + + +def build_list_by_resource_group_request( + subscription_id: str, + resource_group_name: str, + **kwargs: Any +) -> HttpRequest: + api_version = "2021-09-01-preview" + accept = "application/json" + # Construct URL + url = kwargs.pop("template_url", '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana') + path_format_arguments = { + "subscriptionId": _SERIALIZER.url("subscription_id", subscription_id, 'str', min_length=1), + "resourceGroupName": _SERIALIZER.url("resource_group_name", resource_group_name, 'str', min_length=1), + } + + url = _format_url_section(url, **path_format_arguments) + + # Construct parameters + query_parameters = kwargs.pop("params", {}) # type: Dict[str, Any] + query_parameters['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = kwargs.pop("headers", {}) # type: Dict[str, Any] + header_parameters['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="GET", + url=url, + params=query_parameters, + headers=header_parameters, + **kwargs + ) + + +def build_get_request( + subscription_id: str, + resource_group_name: str, + workspace_name: str, + **kwargs: Any +) -> HttpRequest: + api_version = "2021-09-01-preview" + accept = "application/json" + # Construct URL + url = kwargs.pop("template_url", '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}') + path_format_arguments = { + "subscriptionId": _SERIALIZER.url("subscription_id", subscription_id, 'str', min_length=1), + "resourceGroupName": _SERIALIZER.url("resource_group_name", resource_group_name, 'str', min_length=1), + "workspaceName": _SERIALIZER.url("workspace_name", workspace_name, 'str'), + } + + url = _format_url_section(url, **path_format_arguments) + + # Construct parameters + query_parameters = kwargs.pop("params", {}) # type: Dict[str, Any] + query_parameters['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = kwargs.pop("headers", {}) # type: Dict[str, Any] + header_parameters['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="GET", + url=url, + params=query_parameters, + headers=header_parameters, + **kwargs + ) + + +def build_create_request_initial( + subscription_id: str, + resource_group_name: str, + workspace_name: str, + *, + json: JSONType = None, + content: Any = None, + **kwargs: Any +) -> HttpRequest: + content_type = kwargs.pop('content_type', None) # type: Optional[str] + + api_version = "2021-09-01-preview" + accept = "application/json" + # Construct URL + url = kwargs.pop("template_url", '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}') + path_format_arguments = { + "subscriptionId": _SERIALIZER.url("subscription_id", subscription_id, 'str', min_length=1), + "resourceGroupName": _SERIALIZER.url("resource_group_name", resource_group_name, 'str', min_length=1), + "workspaceName": _SERIALIZER.url("workspace_name", workspace_name, 'str'), + } + + url = _format_url_section(url, **path_format_arguments) + + # Construct parameters + query_parameters = kwargs.pop("params", {}) # type: Dict[str, Any] + query_parameters['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = kwargs.pop("headers", {}) # type: Dict[str, Any] + if content_type is not None: + header_parameters['Content-Type'] = _SERIALIZER.header("content_type", content_type, 'str') + header_parameters['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="PUT", + url=url, + params=query_parameters, + headers=header_parameters, + json=json, + content=content, + **kwargs + ) + + +def build_update_request( + subscription_id: str, + resource_group_name: str, + workspace_name: str, + *, + json: JSONType = None, + content: Any = None, + **kwargs: Any +) -> HttpRequest: + content_type = kwargs.pop('content_type', None) # type: Optional[str] + + api_version = "2021-09-01-preview" + accept = "application/json" + # Construct URL + url = kwargs.pop("template_url", '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}') + path_format_arguments = { + "subscriptionId": _SERIALIZER.url("subscription_id", subscription_id, 'str', min_length=1), + "resourceGroupName": _SERIALIZER.url("resource_group_name", resource_group_name, 'str', min_length=1), + "workspaceName": _SERIALIZER.url("workspace_name", workspace_name, 'str'), + } + + url = _format_url_section(url, **path_format_arguments) + + # Construct parameters + query_parameters = kwargs.pop("params", {}) # type: Dict[str, Any] + query_parameters['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = kwargs.pop("headers", {}) # type: Dict[str, Any] + if content_type is not None: + header_parameters['Content-Type'] = _SERIALIZER.header("content_type", content_type, 'str') + header_parameters['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="PATCH", + url=url, + params=query_parameters, + headers=header_parameters, + json=json, + content=content, + **kwargs + ) + + +def build_delete_request_initial( + subscription_id: str, + resource_group_name: str, + workspace_name: str, + **kwargs: Any +) -> HttpRequest: + api_version = "2021-09-01-preview" + accept = "application/json" + # Construct URL + url = kwargs.pop("template_url", '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}') + path_format_arguments = { + "subscriptionId": _SERIALIZER.url("subscription_id", subscription_id, 'str', min_length=1), + "resourceGroupName": _SERIALIZER.url("resource_group_name", resource_group_name, 'str', min_length=1), + "workspaceName": _SERIALIZER.url("workspace_name", workspace_name, 'str'), + } + + url = _format_url_section(url, **path_format_arguments) + + # Construct parameters + query_parameters = kwargs.pop("params", {}) # type: Dict[str, Any] + query_parameters['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = kwargs.pop("headers", {}) # type: Dict[str, Any] + header_parameters['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="DELETE", + url=url, + params=query_parameters, + headers=header_parameters, + **kwargs + ) + +class GrafanaOperations(object): + """GrafanaOperations 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: ~dashboard_management_client.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 + + @distributed_trace + def list( + self, + **kwargs: Any + ) -> Iterable["_models.GrafanaResourceListResponse"]: + """List all resources of workspaces for Grafana under the specified subscription. + + List all resources of workspaces for Grafana under the specified subscription. + + :keyword callable cls: A custom type or function that will be passed the direct response + :return: An iterator like instance of either GrafanaResourceListResponse or the result of + cls(response) + :rtype: + ~azure.core.paging.ItemPaged[~dashboard_management_client.models.GrafanaResourceListResponse] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResourceListResponse"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + def prepare_request(next_link=None): + if not next_link: + + request = build_list_request( + subscription_id=self._config.subscription_id, + template_url=self.list.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + else: + + request = build_list_request( + subscription_id=self._config.subscription_id, + template_url=next_link, + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + request.method = "GET" + return request + + def extract_data(pipeline_response): + deserialized = self._deserialize("GrafanaResourceListResponse", 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]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + return pipeline_response + + + return ItemPaged( + get_next, extract_data + ) + list.metadata = {'url': '/subscriptions/{subscriptionId}/providers/Microsoft.Dashboard/grafana'} # type: ignore + + @distributed_trace + def list_by_resource_group( + self, + resource_group_name: str, + **kwargs: Any + ) -> Iterable["_models.GrafanaResourceListResponse"]: + """List all resources of workspaces for Grafana under the specified resource group. + + List all resources of workspaces for Grafana under the specified resource group. + + :param resource_group_name: The name of the resource group. The name is case insensitive. + :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 GrafanaResourceListResponse or the result of + cls(response) + :rtype: + ~azure.core.paging.ItemPaged[~dashboard_management_client.models.GrafanaResourceListResponse] + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResourceListResponse"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + def prepare_request(next_link=None): + if not next_link: + + request = build_list_by_resource_group_request( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + template_url=self.list_by_resource_group.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + else: + + request = build_list_by_resource_group_request( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + template_url=next_link, + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + request.method = "GET" + return request + + def extract_data(pipeline_response): + deserialized = self._deserialize("GrafanaResourceListResponse", 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]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + return pipeline_response + + + return ItemPaged( + get_next, extract_data + ) + list_by_resource_group.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana'} # type: ignore + + @distributed_trace + def get( + self, + resource_group_name: str, + workspace_name: str, + **kwargs: Any + ) -> "_models.GrafanaResource": + """Get the properties of a specific workspace for Grafana resource. + + Get the properties of a specific workspace for Grafana resource. + + :param resource_group_name: The name of the resource group. The name is case insensitive. + :type resource_group_name: str + :param workspace_name: The name of Azure Managed Workspace for Grafana. + :type workspace_name: str + :keyword callable cls: A custom type or function that will be passed the direct response + :return: GrafanaResource, or the result of cls(response) + :rtype: ~dashboard_management_client.models.GrafanaResource + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + + request = build_get_request( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + workspace_name=workspace_name, + template_url=self.get.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + 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.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('GrafanaResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + + get.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore + + + def _create_initial( + self, + resource_group_name: str, + workspace_name: str, + body: Optional["_models.GrafanaResource"] = None, + **kwargs: Any + ) -> "_models.GrafanaResource": + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + content_type = kwargs.pop('content_type', "application/json") # type: Optional[str] + + if body is not None: + _json = self._serialize.body(body, 'GrafanaResource') + else: + _json = None + + request = build_create_request_initial( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + workspace_name=workspace_name, + content_type=content_type, + json=_json, + template_url=self._create_initial.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200, 201]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response, error_format=ARMErrorFormat) + + if response.status_code == 200: + deserialized = self._deserialize('GrafanaResource', pipeline_response) + + if response.status_code == 201: + deserialized = self._deserialize('GrafanaResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + + _create_initial.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore + + + @distributed_trace + def begin_create( + self, + resource_group_name: str, + workspace_name: str, + body: Optional["_models.GrafanaResource"] = None, + **kwargs: Any + ) -> LROPoller["_models.GrafanaResource"]: + """Create or update a workspace for Grafana resource. This API is idempotent, so user can either + create a new grafana or update an existing grafana. + + Create or update a workspace for Grafana resource. This API is idempotent, so user can either + create a new grafana or update an existing grafana. + + :param resource_group_name: The name of the resource group. The name is case insensitive. + :type resource_group_name: str + :param workspace_name: The name of Azure Managed Workspace for Grafana. + :type workspace_name: str + :param body: + :type body: ~dashboard_management_client.models.GrafanaResource + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be ARMPolling. Pass in False for this + operation to not poll, or pass in your own initialized polling object for a personal polling + strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no + Retry-After header is present. + :return: An instance of LROPoller that returns either GrafanaResource or the result of + cls(response) + :rtype: ~azure.core.polling.LROPoller[~dashboard_management_client.models.GrafanaResource] + :raises: ~azure.core.exceptions.HttpResponseError + """ + content_type = kwargs.pop('content_type', "application/json") # type: Optional[str] + polling = kwargs.pop('polling', True) # type: Union[bool, azure.core.polling.PollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResource"] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = self._create_initial( + resource_group_name=resource_group_name, + workspace_name=workspace_name, + body=body, + content_type=content_type, + cls=lambda x,y,z: x, + **kwargs + ) + kwargs.pop('error_map', None) + + def get_long_running_output(pipeline_response): + response = pipeline_response.http_response + deserialized = self._deserialize('GrafanaResource', pipeline_response) + if cls: + return cls(pipeline_response, deserialized, {}) + return deserialized + + + if polling is True: polling_method = ARMPolling(lro_delay, lro_options={'final-state-via': 'azure-async-operation'}, **kwargs) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + if cont_token: + return LROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + + begin_create.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore + + @distributed_trace + def update( + self, + resource_group_name: str, + workspace_name: str, + body: Optional["_models.GrafanaResourceUpdateParameters"] = None, + **kwargs: Any + ) -> "_models.GrafanaResource": + """Update a workspace for Grafana resource. + + Update a workspace for Grafana resource. + + :param resource_group_name: The name of the resource group. The name is case insensitive. + :type resource_group_name: str + :param workspace_name: The name of Azure Managed Workspace for Grafana. + :type workspace_name: str + :param body: + :type body: ~dashboard_management_client.models.GrafanaResourceUpdateParameters + :keyword callable cls: A custom type or function that will be passed the direct response + :return: GrafanaResource, or the result of cls(response) + :rtype: ~dashboard_management_client.models.GrafanaResource + :raises: ~azure.core.exceptions.HttpResponseError + """ + cls = kwargs.pop('cls', None) # type: ClsType["_models.GrafanaResource"] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + content_type = kwargs.pop('content_type', "application/json") # type: Optional[str] + + if body is not None: + _json = self._serialize.body(body, 'GrafanaResourceUpdateParameters') + else: + _json = None + + request = build_update_request( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + workspace_name=workspace_name, + content_type=content_type, + json=_json, + template_url=self.update.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + 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.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + deserialized = self._deserialize('GrafanaResource', pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + + update.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore + + + def _delete_initial( + self, + resource_group_name: str, + workspace_name: str, + **kwargs: Any + ) -> None: + cls = kwargs.pop('cls', None) # type: ClsType[None] + error_map = { + 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError + } + error_map.update(kwargs.pop('error_map', {})) + + + request = build_delete_request_initial( + subscription_id=self._config.subscription_id, + resource_group_name=resource_group_name, + workspace_name=workspace_name, + template_url=self._delete_initial.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + pipeline_response = self._client._pipeline.run(request, stream=False, **kwargs) + response = pipeline_response.http_response + + if response.status_code not in [200, 202, 204]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + raise HttpResponseError(response=response, error_format=ARMErrorFormat) + + if cls: + return cls(pipeline_response, None, {}) + + _delete_initial.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore + + + @distributed_trace + def begin_delete( + self, + resource_group_name: str, + workspace_name: str, + **kwargs: Any + ) -> LROPoller[None]: + """Delete a workspace for Grafana resource. + + Delete a workspace for Grafana resource. + + :param resource_group_name: The name of the resource group. The name is case insensitive. + :type resource_group_name: str + :param workspace_name: The name of Azure Managed Workspace for Grafana. + :type workspace_name: str + :keyword callable cls: A custom type or function that will be passed the direct response + :keyword str continuation_token: A continuation token to restart a poller from a saved state. + :keyword polling: By default, your polling method will be ARMPolling. Pass in False for this + operation to not poll, or pass in your own initialized polling object for a personal polling + strategy. + :paramtype polling: bool or ~azure.core.polling.PollingMethod + :keyword int polling_interval: Default waiting time between two polls for LRO operations if no + Retry-After header is present. + :return: An instance of LROPoller that returns either None or the result of cls(response) + :rtype: ~azure.core.polling.LROPoller[None] + :raises: ~azure.core.exceptions.HttpResponseError + """ + polling = kwargs.pop('polling', True) # type: Union[bool, azure.core.polling.PollingMethod] + cls = kwargs.pop('cls', None) # type: ClsType[None] + lro_delay = kwargs.pop( + 'polling_interval', + self._config.polling_interval + ) + cont_token = kwargs.pop('continuation_token', None) # type: Optional[str] + if cont_token is None: + raw_result = self._delete_initial( + resource_group_name=resource_group_name, + workspace_name=workspace_name, + cls=lambda x,y,z: x, + **kwargs + ) + kwargs.pop('error_map', None) + + def get_long_running_output(pipeline_response): + if cls: + return cls(pipeline_response, None, {}) + + + if polling is True: polling_method = ARMPolling(lro_delay, lro_options={'final-state-via': 'azure-async-operation'}, **kwargs) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + if cont_token: + return LROPoller.from_continuation_token( + polling_method=polling_method, + continuation_token=cont_token, + client=self._client, + deserialization_callback=get_long_running_output + ) + else: + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + + begin_delete.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Dashboard/grafana/{workspaceName}'} # type: ignore diff --git a/src/amg/azext_amg/vendored_sdks/operations/_operations.py b/src/amg/azext_amg/vendored_sdks/operations/_operations.py new file mode 100644 index 00000000000..9102d9ad46f --- /dev/null +++ b/src/amg/azext_amg/vendored_sdks/operations/_operations.py @@ -0,0 +1,137 @@ +# 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 functools +from typing import Any, Callable, Dict, Generic, Iterable, Optional, TypeVar +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 HttpResponse +from azure.core.rest import HttpRequest +from azure.core.tracing.decorator import distributed_trace +from azure.mgmt.core.exceptions import ARMErrorFormat +from msrest import Serializer + +from .. import models as _models +from .._vendor import _convert_request +T = TypeVar('T') +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, Dict[str, Any]], Any]] + +_SERIALIZER = Serializer() +_SERIALIZER.client_side_validation = False + +def build_list_request( + **kwargs: Any +) -> HttpRequest: + api_version = "2021-09-01-preview" + accept = "application/json" + # Construct URL + url = kwargs.pop("template_url", '/providers/Microsoft.Dashboard/operations') + + # Construct parameters + query_parameters = kwargs.pop("params", {}) # type: Dict[str, Any] + query_parameters['api-version'] = _SERIALIZER.query("api_version", api_version, 'str') + + # Construct headers + header_parameters = kwargs.pop("headers", {}) # type: Dict[str, Any] + header_parameters['Accept'] = _SERIALIZER.header("accept", accept, 'str') + + return HttpRequest( + method="GET", + url=url, + params=query_parameters, + headers=header_parameters, + **kwargs + ) + +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: ~dashboard_management_client.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 + + @distributed_trace + def list( + self, + **kwargs: Any + ) -> Iterable["_models.OperationListResult"]: + """List all available API operations provided by Microsoft.Dashboard. + + List all available API operations provided by Microsoft.Dashboard. + + :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[~dashboard_management_client.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', {})) + def prepare_request(next_link=None): + if not next_link: + + request = build_list_request( + template_url=self.list.metadata['url'], + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + + else: + + request = build_list_request( + template_url=next_link, + ) + request = _convert_request(request) + request.url = self._client.format_url(request.url) + request.method = "GET" + 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 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]: + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = self._deserialize.failsafe_deserialize(_models.ErrorResponse, pipeline_response) + raise HttpResponseError(response=response, model=error, error_format=ARMErrorFormat) + + return pipeline_response + + + return ItemPaged( + get_next, extract_data + ) + list.metadata = {'url': '/providers/Microsoft.Dashboard/operations'} # type: ignore diff --git a/src/amg/dist/amg-0.1.0-py3-none-any.whl b/src/amg/dist/amg-0.1.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..221243c3a78b86fe3516169af788dced9d69026f GIT binary patch literal 42653 zcmd42Q+%aev#%X=Yn9tINH&(aU|S^8mjz^Z-g51YV}M*-Q(7m$fmAu;9I*QEvZm(>p0!E;!sN`pxI0|nxb z7^l_T-RNkRajeaqVi3*3%Ws}|t<3I%JhbNW1b0YAIUYli{aC`Ym=H{cgP-+E4Hjdc zO@~KKqgY^XJud00O2#P;ff@NVTh@Dt?-M0xSIgh93ShbOw>*3c{D6eL#SKaQ|s& zBWnv2TW38}10!cUM~}buj_0)*4h*08Z#V?+D^5Pa_BdrN>WPcp%3802dNqQfm0^j*@09uY4q(&=iy(%;U-uc%4bH&=;ge#r2`(BN(rzu2`zNqP)*8!Gd!Q zA#m@zwZRGL1GL}`XF=2i-O%h&;UM=^b(Ig^TKVV|%SQ6PpD2v0*ybH(rS-h#znHk>mHRA7J2uJp0?8wW4bF5+;M9CR}*uDo>Of2x%`o zJqjF=5Qbl-P**sRXMGo#42$b&*EM2R-RuqrRiq#k;YNvI#;VSJ`z8*OMsrBTfn@ux z-oBM!*4dvee)x>!u{gaRbU*;K?xzKc#w#NlPvLM+5TIEAspVM{^NY1Ta z2;bO4hi+@flB6{!mLj!*-D$3k9geYuYud$H?rCfWgH9Q({ZV`1Zd#qQc0?wG*RS!6 z^do_Rt_L=pK458zgT{IbdgKzwsj(LV7lRAR0Hfp_lf4u-jZB6|Yx{kbFQ=e9>{K=M zJBEmqNO-&eU%y+=RqBBU7cifpCnj4w7HzY9nkZ%g|M1F98O_>mw&j!ay8D4L?4Bi) za_p6Zp;9Mu@~52E*u0F}h`?O?bm)cC6zMVAn8R%pnNJu}zDZ|G2^^Vl=nxury1Pd~ zN%-0kke1{U^_r(y;bwbD7S~!Ei$klofEF?Oju^S6MsnIj{!lOx##V}(J7j~jttcx z0c(0+kHz)Yr$ydY&9@Pl{nM$aRakWFG6f;O`e8LgS=!x9=BS(DKR@pu$Wt$* zw*f#*4m`|bE}ucx3o2aKRn+8DT^1_j)W!HNu?|R2L=qte zE)Xe9pjTCKp_3VR4J2exz0sb%HtF@{^0yZC-A)%U{ephZ3Z48!`a{)V2G+HX;MU8K zY|7S4t1@NLRt1u`bt)xo6Pvq(scz=L?59)CTT{`4le+P+P`Js;K4moqy0RPWtFY36 zGBG~7oWQ_v0h9Wm?qPSH(DAVy(lU-PaN${&!%ec^w&{9gn}@G)OZf)z-*PI0ZKHBT zDmA?Nl{3L30s;Narv4>wvNv!vuyOh;qw4>u1G@QNI^bI$qnU}KzoGvNED#J6nq?#G zU`?UE+BtPap;$~wvRki@`_G8nJF=H3DnWSpmk3;N9>NvVKXv=wgZsAKuU0ss>1WGt ziyi#*?1FW^usk(ZSTeRRVHF`nfamS>qG3||D0gH5z?jPfzz;(291hv|We@0u4@tYj z=F22kAA$9j>oQ$0;S?{mJn*P=!_RG^^mv(%o&DIHi<`L5jSJHih} zC-_|x@?^492<2_LNLNl!=I{;gSd;7fKu*H54x26xHoEqjV99tkw4`eZK2yzf4br^+rJn)Fp)Xlf7N2Mwu@UDi5Pzv^y zV}j;s;=@22$jW*f?bt%+){3@hGsEAoDNN#*q9xtxrEhYYupM^_DROir34^l^GEnGS z!Yr+<5PKnNd^CwH=gvw?lBG2{%DHHNJQKW$XzQnz-PK_c*5exzGas~G7S*YBki4$q zmZV){Ayn-{QH^13=EMfpXH+kY%3T<|miVUe)CXin(i4c!P09g}2<#<{75Icr|z7Ie~Gp8JH~< z8j=Rl3Yx_U9@uW!&RJXz0sIzrti5f;?040lk}IyQByQ$QY=tW4y&2`?Dg;wg8e(bg zm^t+~$c<$eL97VE zA2)^-7^bCo9Ws5?0y1?>;@W>C8VA56_bXT!c=#F1yKUh)Sr6Vk_Kdd^q6{QfuIBQ1 zM%QG`7lXQv4#e{AGji$tc(3JEvEJ*3I^ub@h8QVF)!zvjhRKHWJtJE}!8y=J=?B+B zF4?2g`O;7KTzmEh_hl~Zt+6)pz+Ny*n9&FDwQ+f@Mfj~WAawJJj(Z1p$-eLbhA1qq z$Ef`>LL|K@T;;g#S>VqbJ{MI=bJFdhqhZ=g7VNXM5@He6ujq z7rVO3gI=ZDzQO>R&Ue~U^*HhkXI41TRn<MeFEoTH0$QCQ)H_jv) z@>7DA>ND7z>-d8tu&X_W8U5f9;I3VVHfxD+G0I$#c(#-E?lRE#HNta7pt$d#g z<$jv2Ue;3{jEga8ifP%umKJ|&$PR}#Lg%ltq7obk2=kw@(AB`&!r0)etoV<3s3L2( z#sJ#`G44mOE{sdCZ~^9s*_lr<>nyyM6FQ(^ygElD4Ip(p4}E`Bblv5!Xu43!vG(QZ z6_?@(aJV=2yLZU*Mf0KQ6q5DYj0c0x+3YY{qS z3>!U^KwN^Hs(Q`Q?8h_wL;--BuZ{lN?=6BB_z6aV8*pN{JmaUT&a&HA^K@NKpe}fz zsT|*jZJ78I3!&5S1lUp%!0g%?ifYPrsm!KU8Dk#E*0mgUS_PI+#8*6|ECzG18&*YS z;Y1H8p6h^(ePJREr5O`qBQqYYQ1d}&d?=B2l;sVwp)(as{Yc@m7Q|^3qN=@Tsa8!< z9p6RlIMKv-U_y_pm<9JLZ2QjjD9y#YQ%%mKgP?&=DqD3X6nlh9B znUfnQ-hU(@-Rb70tK)yrHJ3F~%eZ#t{?TSP_AOVruK95_Ronw>CNBwO9tvtk0&pM> zam4_R)I}ybB_PEPmY$a>Pl9-qSaHR{ogSp2OLy6{HnLGh--Z$Ov1i)}vDI)By)|$z z(!;PCmR$CO>Ie$XAK0(ISPx>i=Umoi6^~3vzr~apY+n6{Te7UOuou!kVZPnp{5)eqqaOJN^+?C`3+poJ4dI|Od|IJeZ zI07Ors&WV2}*Ff{xJK+fx?*PPf2y$kFo&Sg*3% zU8ybRk|n5}md*tUGxczkrXAvhQ^Dw+CnNT_9_Q|9Z?a+R~CQ2)`qVqzai4y2h zf7&L~h>XzNK9S+wwk+L^IX6r6^Sm2_j1}Z_GfGl7K9r+s9P)b+K24uJlO@r=Ms{@O zd(H#(X}p3p>HHnWb6IO0K5K}QIL`}L(Vo_d2?J3nhEB=a^!YD-z{29HLcpa?U;LFm zdddI+q5Q)cja;0Z?QFg>$PJ!nyY<%0K8)i}0Sby@kNa`gKO{8`o-lTv*8Ye7S#;>! zSJ2JF8HzPX_aYPGpr4nQukI0Chd0-eI-nEL6o4(Cglr7smuNpdzx%-Mg9Duu+EiAk zL@19uQZx*2U12ZPo5&?BdLhTuBN12RV#7ey-!yOzS9O|$&;v^IsFYO_J;wX*T{ESe zBz=sg_o)dWvz6_~5?FlmDD2|+AO zr=bJS0KA@GOZjm&I*X~O)=f2#N9=f-*FBwyQ=&@r63x;^pm7Qye^{DGklId)6Mt4p zo+PjLBNF!0F;_w`mhTD#JiMwpdL*ZOg{oA@I4ZEDpIm^V>asA;f=;F^5>!?dgCv%L z-l`MPv2mh8%f1M5&Qi2m1SVIHnUyhev~|FAyRKU!n_V;kEh>&wc~l@qt$MZ?kRVx% z`xA%!{#;P}$KYdM_a|=N=iS=a*38z2ao-!8A_|Tnv7T0==WgY-+y?qezohq{&g^wfEKG1O+hLYH z>iDEHPLx%Vx&;H@gVU@N&>GN1bVp<}2HW&ipn0w8hVUyibR9`RSiJU#I^Jg%9MA7b zvfb(9bOG{lzUcrw`Kd@9v$IhvRbfkvSoY%6sVfT@`H?@RR4l;YGWaN*>gi+w*yidg zo`B6+=&^5`^w8G!q!efjCJQ-$=f-HvN5EyqqQK zOc|GFoxy^dWdkxi)k>{^X}KRZ(Voza(yphDA$< z)2&hsinM3WS1~pKq;J_#O=n@q^$`enG<&7{2Y`H9^9#X7^}abFV-5(##RPL4J~(Qi ztT7PeEwRnhez`DgBYuC|5A?S`Wn?-hFfHVV5ROtXVxBPpv4t8VR>d%K%(9j}Wh64} zp6KsFGztmY^scpgX)29%mFK;JUmtPz62)*iDZsxb^6z)e6^J3vMDfgFLh(c>zUw1H zUKe(R+2loEc>}#lEc1tdj?j2qdL>1ut_J@0JdEYW(wzK8tVVa=sq;sZ!ub)NU^gD! zP0%s(jxf_2BEl3A{|BN4$1%7;*NjjAT(*FC`8VQ(Edw2>S8+E=I=_W7 zi$gQ=@0Caw?95S-lGw+Xwz2fny{V=x{?DVaTenZAhv$tkgrs`>*z%E|1@}MLdQ;kc z3(*+xm&xu1;}OrhXL}XX`mjLoZM?9bukb~(KYE*-Q1~n28E;-yu39LP>3(JjLWlCi zjxJw7i|zB>?XmrM0==w(8ZqAq2kMykLpt7pYJfH5pvteXlL&v-EgNWNf_H`!iD&q< zaoSrwF%G+#j~n{56~Sm+9)E6Lt*6qgQNMlMpX0ns!OxQGip83IqHv;0U=Y3ki5&ru zQM~jpLo(OIZqXy(m`ej1R$VKO#Firk1qvGlFzMgE&Q@bd+&3_|GTt_2fgz&At)xL01s5aIK{0L>^*OC;~MLen+6P_{Lw zf9N&QND({H7}&t68fK^q9Sqo4jTk$u`3F2L&n}`G|DC`B4vvA9kP#Nw4^CN8eRKrH ztbmuK+&`@gyds9V=&S?gDKwvWRR&0dtg`eyVFaZ?(=pjrIXonynmNomIytwv$a?Sf z@b935b(${9CN62Xlr@xzAT?A%D}meJ(6|>y!H@>SG)CGh!`HN=W@>i|jsy{Lr8Vcd zzkTx`#HhrJij_-~dvi|xn3JeeV`J^;CT5YHyMJ;;!cW9YE!#A~nxBJU8Mk7mJDcI3t7N8VTl( zuYdZ1nMA$vmI(lJYwniP-&gvsEt;dtbS0Z zGt|ba)uXI<3ZV?1fQ{4=9M72Ip zAaV#t{KmAk0fmlH3Cqm=b{i~7A6Vsag==a3BY-rr!|;!!=mgkg<(Y0VNwL0H{{uo= zX?u!*>@s#g`DxETrW-pDwbh}UFGJ@YoG+YU&{X(bLU$VR#bxl{2dI6C`zc$RZ{g10 zq9$%%`AlMR7e0Bbu+CSmDsj>>bbBJv|Fr1$mym+iZr4&Q6;o;|tVj0{m-rjJgFo}0mfV2 z{h2d&l83RC3jlc1E$E-%tSexZo0*Du!v;N4$Yqq3PZ9|_ zYuz6pw%6rVRVQ4$D71bX$;wr$@%SjUrXKY2bMF)hK?SYlN`mX+!ee} z8%hg`u`?&r@%_Horn}pL9>BWa1 zNv9o^R?=OLZtxV6V@*Dz_gE~!Awm_`-iN0UpM^#BRwiS-kCkDA)G}aq3;Pu%Rao{y zRDZPBoK1)TXZ`phWB0bN1*AE-O};!Tctfx0LJJEVrfE)--E0{X`!0NMkP>;y=LjyC z4a!IwCLD~V%Z;7*Rj^fkj4YY{@4Q6h;^G<+L&dg6x|QMVxlDY7hh0TI#DCW2F3F>3 zEXMJ_C5$-p#P~#2wCkaunrg1ilj?m&y$~%dttsD_SdYB&89%J=nFg_O8Iw`-ZZj}B z0V$ySnXYC}O%?~97z)H@qXl3SQy2PC!g+4L<=|d|!uj9ks>Al@gAIUGhMGu*aG8XL z4U&lNi)DlElw~WXHwE}+)daK@>{r zEa>3Mc})wGz7ZM@D&q6-f2tD?RELQeA1dU-(yZPCZ?zK_r?X{~eju8BX|L<@nUi>( z&G%7=3Mge|#(#aqD5Z<|6K7|WGjID{!)$4*el|IAI@8r_8Co0m4w3)D=W!T;0Y&Mj zPvUnT9t@)$1v{t7qs*!@m&xwJHcKRzKt3)$F0TG7wcP7i^+sSVRF*$Pr-x92TuK>e zk+iS%J(;|Ra6!VqY=Zc-6qp}>no3d|Tz=nC-E7}xx71VmiMC2V3uu>VZgzb9CW8}k z_oLtq@+|6FtBSie#Bfz8;8W0BV?iG(87T3O$60@RinbJ$>7L{GA87v7lmlEBOWRm@=7Epwr{b;P7-_>(=Oo~ z9W&$8UO|Pg=%V~a?mG@keN~kA`RdwOc(kbKTMf zZfW-KKc&bEvZ@L(RlSgj3;ZwRDwnl?p67HtO4D1@S%Fol9{ud~6-8z#F0#A6{Nt~^ zcW6T~$x{VN4nPb*PuVBtm(tyIKZw$EokEoq59Q8!M9C!|CHGgNyJ`PVn$5=T;O zxgPx_y@l+Eb~=pjr#LxubIUV*(c0<-RbUbZV3fonZTC7dQ^=#>n_16QX?2M#8GxC% z-n?9(ic;qfvJ79po5SOGE-(^0)IrwfJ+m_q!CXTIf z^Ue`>NASe)bV!+vcrS(oGg(}oKFz{MVL8dGD+fknidAb-@t)+wj0U(I=d9w#gRa1= zE(|A=z#kvU1;1|pg_ySyC0}}tz7jrgoa(4RH zsiK-VL1++0SkcEowH*CYu;OWsgwBrDsY#~MPCx{Y%J3{~dOSkcfdb1PKkM5fm?l_WPlCtJv+8R=PTWeQ1=Ne%Sh zX4b64Wpl&FhA%Ems(QBg`nb`PYOfi;;^=&n&VA)Z$;Kp0s21{-EWm}N5^VP)#ke8Z zTh`p{^~rl;IUui3Wc4BzrB!H+OmfGFSbif2pR7hWZ>MMsJkU5St7v+&_as&p{i+p- z4v&vgP0vfx`ED6E;h7dgXej9bM^vdBp>sp}E?usX8UYznpbx7Q|GWP113H>`EW%jXTE zF2QL~KrrWiN0HS$NtijtLASV3tgigl<|w7X@?xCqWCDCWS}h*G!!K=P&mkv+=8}Ib z7iBWXq}vHStS5PS`+Hi$Ol|aH%nLm;>?d(!o@3ZVu4QXSK5p$i|37aBHzBte(U)%> ze2qT+UwzBQ>>s|Rq-eFy0P9PC#}E0k{SA5OL4!kMWt5_tVbu#~$UQdkHpd8TMRN`B z{j$laXS1aT&r9PzQR3y!C$XBjd}~%t{^vF^+@p1uXz*aNy$PIhK3gdV!Z4TC4KiJJ z5EQCEBf;mO*x_#7Qa-|R|1pknI81QW)H55+4*2y%gd0+I9s}qCvg|jq$XVo|HGL&M zk9RSa+rIOwGV)e=X0iOMJ8n-L;?VRaT4jWn$8n07CZ;gqra*CHha}G>Ym3}nG;O2& z1UO#<>@d6NAk%%Oh3woP{98X3%ciW-Nhvvx+v8u%#jqB&i~Y;1T^SNX6+GGVKID_KIzhD7M$lVENV6@f zzV8vTE*ZAuNwmcKf7F~X_BmL%3>!90nj&bZe-cug-c+mi_;WBVyqYcB3~rCJw>*Fm9EXp z^Gj^Zul%7QGsP{VWi!i~-*WG5Bc)bCu}*-7WusbvUTvt`0scQ-El5nopyEqEY(Rm4 zi2o7BTup3^?Ho;v^_+~Y{;4I3|Ise_YAy03ju%V}Jh)KI3eTbm2bhA+8>AU1m_)R; zXcZF9Nv_xBkr3O`cP&C_lHhL@0od~;aDH#btfT5JRl%X=J&?9~VUyXd#u`+)+A~Zl z=Z6|?BJ7jmIFg>d0+{|bPCkM=Wv1oD3Si0&oUwWB!|dJPJY>_ZqT9cD8!*rrfy?_T zGP)CmhVtAolPwt;o6UJ}#LN~BaY(3Clp%oH#&Ivl$%w=9fc8xb+&I~h>qG%pXA4X) zHc{X3LH%ByNQxMox8TTn>clQ6lQ7QYDUk9e>v2`2=U;ohue>%cK-CJZB|B-UpPxXd z0*2HqlgQvg z-q56eW8?mb^Zi;>k=#o+K%a!_+b>Fyw}0hVBQ+8_EW6F6~gV#QML_cs;$4L zE1{HAbm4v|Y3q8y?S!*@6M#C+M4~A9fEu!+D0*+qOKEx2&RqILkQ9tuDZaKHbOn~; zTKMkR_2&F}J;xP)m=kSwWF{~JhWZ_P`3J#aW(`wzT3{+;?h<5@5{Xo*D0lv;1%wu5 zv`syYm6Jy*4r-L^`l!7xx`~XH`7R+Qx&Ea7$(zy12V4?GHD6B$>rBUNi z>S|O70cOlUmHZsU2XwlACyP_EVtg`_dW=c8Eu))2*!Yt)il!eOfpa7%#>|1;&hIYYi=48R$;{}{A`+&5{KRlfk zVV32-hM16UQjwF6~IXD(Ee#k&qn7hG(&`P01;q-d0 z%~mf;lwxm$vrWu$Y8G*WtBf=DC2f3LVeX<`$%Hu<34|o)7~asXRdh{M5wyKFDb?InRxl`l^>kKsk0MT}D7O3{)AO08 z=H#2Bh>pmi0o8#i=I3m|SYtWv#ken1MEY9QX#)?}Qjm(}DHQZl$08gG+>3jU?}kQ7UavWo)WIA4&oAsc6qSj10|f5s=wMpDAQuY@P|t4+)P@8XlOfs?tR zoq?mV-d6+Pz|6$v??C;3&ei{oR_Z!m(F)b)Z?xLs8>U0;SXMUp0UX#Y8^k8NQ;HdC zfM|X)c}NpMCAp#)cHefHm~1T7`WvK5k4CZk2mshbxWO7G+8MFbJ-NF%cwS*{f3x5Z zawVSBx>?j3;>hjijVnX%a@P%KzCM8@8G4EosYr}%;eE>5);V4Fk%Q6DB}B%j8Xq8IF;LoT zn>F(1ZFg(3D2khOnhZmr9o2+?P(=Y?C}Z|WYgbZ}jMPZ#_nJFNg~%Yo%Tf)jui7OlOC7o^X;ERp+d?V9#K#{us$hFH%LmsLMzev*H*~=qS zP*qppD9qSYcvI66H52oV?kLza)}8l4g|it;ONvQjOK_^#mkMISGFc;(1@ zvjW&bSX)d`Ub3c}^F2GJOw%QhLz{2K_lO60WfZN&JT*gMgsCpwj`K6{kK4_+KMx^z z@deo3Nt2=zE0jIJy+#TIJY}pa8dhu+1!1Im3mL!y0Cnd8HuKa>gopDd^e_mo(*(su zrIEz7=oojQMglYiKh!t#l^T!kQmIKwS!ODz2jM?yJkoO=#{H;b( zkm`$k^cU6O?V_4N6?-af6&$9Tb{i1gDXBdnv>Aiti6m2H;Cr{bJ`yejCaR?dQ;m~q zGf`21i%n~U_5n|Fid@R;d{ivbZK^`EK-S8(aPHBTir~znZ$X}65;?}?)e((Wo_+2> zeVD}x;bN6wmYRCHWx>!^#-++g9rMmiku`LSt0YnkA>k7Cc%pmYgOIVB*k^k0+n8{L z`uo$|WTlTCWl%pN!XB@mZ>M7?&`L-bec5>erl_9?ptJ8YT1Juye5?IqA7Roj@q-Ar zp0|0AC1l|t+p9D2U1J=&COuve{foFCJ09zKOK8RRJs}1@dol)=E}qHqA-I&=?LL#=ljJ932$QKKsga0~Iew6P(2p z7c|M%m|FIU*hq|cs;@OikJ9tGe;%*6k z%;ini@@kf4EN5T}ntL?D!8oy+=&3elq_*fr8A|>BMr)WL+o!^_semqh-er-gIAm|? zwQbLCrKoso-@|8y)0v3b(%wAw3_tE#27k1sk3Up|v+Pn^f|x#>o$2@ zOskm#)!Jv?J|R+YZZ|pGBzf7CSWd0k?&6~HYr;ad@q4+==9Sc}Xj%T03K38V$@|Go zQo5<7a~n9V&`gkY1qzHUJ*4i^(+#_)=)(uoWi6dJ(P$o)lKZ@MUurO&gv@tLgt7;P zXk4EuqXJ8txMpN!7sJlI0$O8~I^5`5dJ$& zus3ivGXER>|7+|KVcCzMQ<|IO+6A*dD3cnjNAkU6?96v4?gZgtDozck-|p=hmk;xIlUl(*CIu;Csb(CRhW3r{ z`HV_|w)vJYPRYO=a3l7iir7)Woz5ZH3f!m~!PheG$7fn1h2P{u5{~tR4Qgst0^$`; zE&m*BdoRtF%W1W{rf)B*vW02uY#^c(EH)kszzM+n)$J5(SCNY|Vnv_5{YXwBh7o1yJiUuxQS&TE zlWfY2vEYcjVI<7~*##=Wu&)#N$$u=CGMw#I(g33kOj4i981+3JBi+ms= zE{W|k651uAVxDPcMlBK=Ql254@J(=#HHhtN3}>N6JjISZBS*}Ezu^D~)*2~@*aD3p zk`t5)>!ltLD%#f$>tzdB#rJ5(-j;2K>8;Cc(HI+G^rv0VVqeR@1$0Z?bQeQCewR}- zG3m2eHDLB%AqY$bz2Qt-!6Vb_6y*1isQFc_ zWBh0x>Uy6e)SKH}Tu$*Nu-1{-D<^DRJ9Md|<_Vv%d{Tme;AKdSA8ouoW3GcTtij3& z)0sn3NPG667GLP1an8kJ(wpG2L6tmA*;lWqatXp~wAc5uHWkkO7J)@?i-2>RdAIi! z^q*HRC!NUU+ZTfx|0)^(NofBE82Hc6zjrTbAa;QPG4wL!1-|HlR5PU?t0D|d$X}lm z)wW7?pDSs!D;955t_%43BKn*piwGWfDXetwjrsHFdLLT1XIZPRc01=BTxo1nn9QkN zHL<#><;dp`4OCq<<+hkrI1xlJ{7JAXQeq!P#HaySPept`J7im4ulyRAw{SM-=e}S@ z`N=q6{++;*XE|+v9JcR_VwMbwc8I-55bPjbm;rG5xl{W|&qmR$|Lr2PGSNX<*at0v=-Xfly^^Fw-yPo#rMlnwP{Ly*kTA+VDJHg8^d= z=Stxk*vH6VhjrgWi@m*~#|?nro%d3_h5=kj^I`D5wAAa$)4EU%OXr5V6@y=wE^wWc zJ1{pUnzboUrn?4;Ex|D`Ya0bgNwVUUCd_5E=+?3&JDSuEy65~Y;B!kTC=g(K?7hGvkulb~;2re6 zz1NoqhO4uJcHYqq&Ta6Tkz8|!tH!(4qX#SJC*0cCJ0%tQAtEjP=@DdkHg5i^Ye~bS z9cK8;h(QW2_u;;&66f6Bn@^20mxA6dZ;Y+68M&{=aCzw7v{`z6>?nLeXuzKr(>?(4 z(A#~J#n&l&B4e*oYCtv9ijz<2`~UDEjv_*qi?6VK{RNo*EnIRnadi4G*bqNpJIH`2 za+z?0K-}IB3}f44FMw7VFGU?v={B}PQX9yw#ohe;%GD700{qn4)pTpx6TCCoY04k$ zd7Eq|Z4G!JYcsnLZ;-{QXR^w!L3+>*TTaUrg_x4tnRQ7Q1qkKti=||y>eUb)RSa%Z zAqS`0^ZC5BpwWpu61{!XV7Kzzp=Saf#8qg@m(JvfTVxefZjYReFTx~In!MKQS8W(I zfYgphkbFuErm<*StR;-A0vOyebm5?9^pm!{m=bGrL2t3?sv~p7inZpVWKPV;{oXwJ zGv}9QmN&NR=;=2Eh{{>5wCVW1zw$2=(|`LpaK~X?Nnc0B`lWc}|3>W$EbRW#8BuJA z{Tg510DZ!TG;TMp??H!HN7Rlg6ll`!LJN^gHJ7d-#YWB{uN3ekhzjDDjv01>(UD3F7TotL97amU*XBNVM+baKsR!Dg^r@4S~5N zMfGWg@D+xxSi$y#@4Own@^19A57Tbr1l@x%gS~`A5+X9*q3UZhp74s=qUmHUK!SS8 z#E_)eK9>y)O(DrKFTjZD2IQ#|gtRP4@aumg$53#jJBt(=AH>7y70z$R)cl{^#ux$2WCtwwB z!hH^CVEL=Poxb|8JWV@IIo8t6aOEQ&Tt+rYBbQKaPRzPNFR!kkTXZ{M6%P zCPeaV=Vgic0u6F^jn?2(r`K5{wY@yEKwFveTz>VP4(rLo;M~}-EB)rO@Trn)!KSat zBu;9wqhNBeI@V%dESXTsZi-pR(mXh@m4*@tRP8$V+ptVN59}3 z_QvOb!MFZoP8<5G=zTT@j04PEU!QI$^fvaWyh{MzQtVlsP=P$p9KXQ}q~CU06Xm-xobkKG?<>u!gc%QqiO>2FEVMuo5DHN(!e(nyIB!*a?bJr?4~ zsG(u8q6cbb+ph!6R2$oG_omO!7ikcuf|G4Gxw(13J{a+7B5N{)yO}jg*=T`m%$X`k zqhI`x=EhS|O zbV#Ihq1k9Tvic!ww0Nwdr%Hx$tLO?C7zoW$`>)BD`K|Fem$HFtr*S1nFaP7q^@y%0 zG12H6{m0LAi)At%nR2LWei#|)?Vt6wt}tV* zX#HVj#k`%2M$z!s#|-$5^)bysX8a=@tOs>8v{z!36`?_6J{3W38s~y)3P$Mjtu`2A_{|Ai z$Zz=Va^WcH=Xh4XSbt!jxOR7*nd z2xBTs1Afa%0&9 z$-uVq3-fb(c**8^!)rf2#%`-#z1vT3ixk7W>iZ_@HAQ2F9P9IV=@12 z0=l**gVY$6gv4F#zBxZUrWJgKoH<$5+P3!2yKz7NPm24~C2j3b>LbxDWF|;-T!MeJmJi(Q0M}~b&zcmmiGEpbqP6nnLzSlP zpz#NDHE!k4p{tg2sr<%s%T-0GAeZjDD4V^PFB~_{h{O6W+vuFa;lFH9S-23o3qR;> z_6NOj|GN$9-{H62|I;kk-{Tvi{K0RF22+4B|1W+k_`z>B&Z!b9MVAzVZ>={HQcR}Y zy5Y<8sb!rv*L?rLZ$(UpMx5nS2M4Q*N#+iBi+*TVQmM@clPQ6QUU02Es!Efg`L3!` zKl>F?m}O2d@;~;g4FfdoV|O0We8ylYld`@l1Uvid&0`EcA~*VzegJg25|w*-8CVKR zSel}st;soeFX}GE8=bH^k-#Ge&EKnm!Gr6ZGStN8WwGd8PeDqa+o;IWOA2*ZN@|=B zDpaPhvyQ`TSuo5A{i(_>&LKKT2>KlG%F&~?P1%nx=Fa|J4GIke%BHYg1P!nz5yigt ze7`%2vhDfhL~;9FCff}E;t;n{)5_Dt?R~|;*Ctl7*VgRpboE{btQ!|Il*BWtr52)z ztrGbf%SaBTBx=O<_4-yzRFrDdKpz5lbu6x5f$JH~fa)FNh*m0}%}q&Xl4l)%AgYQ) z&hfcl4?9w8A*{FCr6s9Ef$FRAJ#%rCs5_6VxJ`m+8wr|q-&HDf49Ancfo@;uQ@0RI zcU}h{*ZQ{b&5t}=;QY8Y{{V5yMP@LVtEtbDq!eil&~ezRM&%}|ZqOT*SM~xG48-P< zz}XDaMsf4r?P8A0ks9s`S(cwJOR?_3{d+qrI@&0Xr2sWFCHQs%Y3UaqXL^eq?+V%zD$idTDc(>Ei*dFLxLvkQj>;$4Mu#2y9!q7p2&7vh1&8f4a+!(tvw*}Xz&Mc9@!roQm`J(`NS%kana44|}|Adr2ek|I`oaq9kn_H{INr-0)sx zaq&C!sjnYJ-Z9w-XE(tkdgPKD0`LL@{)k6+%waYk&3J<&S6^HO;9LnW@{WlnJjGYh z?fpXVv$k9g(?3n0B91K)H=M)Q-S-`L<6nL5#&&Cs-Uv=%`C1&*-=g)%G1#24thovY zf2U?AA-hUO0=&HhQ{{tF`C-@zg7~t%!3u>}pS!I$SblR@^nC9s)h?AmHHy3vPcA;L zK*S6J&%molx{tj1IHrlBn97iqPkS;oqNPO379%&-(ui%Dj$gAFPF z;)478aDRWlw}g;W>Sf-fP|nH;DH<01NBub%0*qQ~@o?Uvb9>~aFN$eYqvF^C;d0wP zkjRH@m>#ih#_1{fY5lkdJ_pY!Nb^1;{qT#A^*jFcwvkb{) z)9KHI*lJVJq~TmhsFo7$U!=dN-TBV~w$3gbdai6OeO%}}>)FznT4ixH8N+tfB{+}d ziE$@ME1k#P*wN`_=$Z!QwV!qHA-a_;s7QTuaM5Btqa2=wr7|i%TVs* zTg#{9v?e>1EfBrXZ1%)~_|{JJP(!`v;#1qS=ZiWKaL2P1m`Yqo&+A5q_>?c(Fs`GY z6llnOr;R25xWvU^_@}q>QWn1xcd)tJEWI_H{l0?|k53HA=bf2TEC|Nt;miWxZAA*e zg@14R1b0t#{6xt)6qcG^V~_(rA2r$(d`Om;|N2jfJpZUs{xgvy{dY&j|4_OAp>qF2 z<^KPqa&P>mizq*c;2#kHO#kNfvv)B4-www=+dB&NR)YdC+pu5Z;FyU5hwai~bA%vR zP?oJqQ?E3e>X%9L*^4JP-cQYTdANnL^71#lmVnd6^^b11%O`xYXlq+lV$I;!^hOsW z#>J}~ShSaI_kcfu#dDBtu*Q}#0Vf1!{P%>7jvq@#RUuL^t>hl|z}@XVu(=0W2KM*4 z;pNbI=&qzUM5Fj{5S!BgkBcr#?7ozcY^qTFo{~-tP>r1RCD<&v6CcuZ!Wfb}wyCw# z*%GK0Y*L3=!hBnvShg&KTFX#4zNx7(lHQc2P+!pZj17Yp#k z&{A)n=`ooKQPqhG|FL(D_8rM29(CKy)3pOuSp=(BZv7o!VSl#KaqSGBf7#-8I*Vzm zU;qFj3;_Um|1Z|q+{w`Fry;HXAM5@DMXzwI?Ke5@y#v3%iQfBXgcKV@`L{4{_nmxo z18uR78r(TR0tKX-3S((5D5%O?E&Si7<55H}5{Akb$+X{vZSEMEv!3Hs{D>Ik7kn72 zk9=Duf(xAMC}I$Ip{QHehi9Jnc7T8lzE$$1W|N9@niVTx+i(M)T~4kdWj z)Wgm~6z6%CleJg6OJ4n+YOI>B)sN%H5oAUV)4zF5z%$L6UgRd5^;`g@RLF(piT&_L z{d34MhYg&?V92O$r9Ueb5KNSr`;v{cY;8IX zya;m>bD&rs^VSBL2Qyf;5N4Z!y#5P)r~(*VtLI~g#ya2+b6IS1HJ z54F2c%T_gkBZSA-NvNcGK2DsxaJy7q&xDjs!21eK&U)0*YlAIXS$r|5<{&pve17Ky z1%+6k*2T@NM}fvsY%;NHe(K%8lil+zq|e;kymV2tsfV*moP*=7w~OeQv$i4R2uE0q zlJW&!gVw{$VH`_6v4D71jnESs7z6aQ$%@R15z*==nh1#)L`7WWun!dT4?_q71Cauy zrBBVKV6(+9+6_FK-$@UNP=)@;5b=~S$vW&7ScZO~^@^ap1Y0#J6qMm$zeEU^9ePNb zcyb-75-lJEM8@G1mtR$zkjh7ck%3Qq%PDqr@P(=?D zP5<`AW2d`MS$seCkI1~v`28Cq2x!% zeU_y?J>!+?(qmrvUk*ZMjOP0H1l08ikWQ(0Xwt4oW68P0PzxJJx|$3d=xM*s{2s(X z0DnS^D$m8#DJal!bW}t`$yL~f;UNd6fkrF+i^9@dWoAmVFmU7yd;1>D({4Z=08H2S z!i7NH5H_Sr>FPEN0idplUcu)Dk_m)n(GodOj0=NgA=EoekX|KH=X93nQw0GSqchI|x}g#MBht=GP=2i7YNuUi3%_i@4i z%RL3}m8p@I%f;u(g)z;Uh5zC@msZ7w$@R8?aBafttZdG%JCVS$lZ>ed9yD`xTlEM4GEZE!<*1h&uq2oO8){# zYZ4bas78v1As%i(qpApE;I`AnwSG5h*(+@`vH5VJgLDO!3SF-(RwjMe(GXolZ5ufq zxfn^hZHaTEb7~8;Ai6iG4s`k#Vj`$G7mZ|}bed{~(HKTe_qu7%z{Mfc&HMGpu+b|( z5nMMyd9u~;@eb7*J;X7X*y_Q2F3@$Vd{87^ww9u3Cx=S3MGjq+uVop5L^g)5w3ZUX z)WA6(PMqcluH_a7NNp+P^_Rq@0K|0z6>&3b5jC>Y+vVO~mAHXhu+gPV#*-ivjn5`7 zOQ5lp|KnQB#UJHO2f4+nBIlEz;2r_hF0Pg~y9L8xewf#7*-S_HbErtNPQ7q9AZ35~ zyh1QBYwn}HHnSqkV{>*|eXT!Md!E5&F`9Yk?hMij`Q}1m?=`u!&;0Fi_DQqvp?hhR zMMcLw+c7ZuE9c8S>w*}*c24Oo3Brjkn=-wf_zYR^txvE{9Uv|m`TSA$c{%2P_F>T= zR{PjMt>|y$0DgwzZOAy?`&mBQ5CxZWoW}2&QqD=CaZxI!FcN24wAR(AH?2&8mnZgS z&Oc38dD(ccEVV1&&T9_AE5h+wVOk%TTY5HD)HmO(wDliX)K^7XT&ZfXhOk2bK6n0l zJ1jpdBrlWi;BtRfSWT%hST9=%pCA3wkMA~Y=+RRau#j|}7x_~`VOQsUtSsB2PGXC+ zB23lBLTSs`rh;RXFR)YIK}`64u5AYH7^l%Iov^wPm#5sktx&mr^3h%$0^hRcbp)!$ zJpahx2X>`HX~}tBE0J`QiuX>&@QAN%+-ZtHbCi)`-WmtoI+oX4)6#xAbZ#fPh>KL> zEuY#WTi~)Qxez-%QFg2^_Q~SiaIu0-HYmiu_4`U!Q!<(OOmb$6(Y4i@f0wv%NY9wj7s2zeGhbh3b!e>)PsU_CLj|4c4mN7s6&8T1$qZHcg zdahs$=JmZdjc3>K#q=0Lh|gLRM+z%R)Fq<&UGF6lA(i!Jvi=48BO?-^hVSC@}NKrjpE_j(f9hei0-qUdQ+ zy+~PgYq0$}(iw7V_|WSE{jf_FjL|R@o+G>Ec4{_)W@(4^+J*%CU7Tnw@YX3v|A6Z; zHsu;m_E5r$?G&`S1Ru9!^Z`Oh3r)9te35u4>n3QFky&U&UbL!yEaVb@fpDxL7l$fu|iqm z*DWbJ#0izVz&Iyk-Xj}id}IPXVI^$ufQ;(Pi>h3Vw7OyWi2crpKuSP!AitRV@m|>| z;GjWlwCdUl7%58U4CX23!PkN>JKd%CdG%mDOByQ+gkWN%bcXDh^fY%6Cb|-_^4u+E zk~++=^YAi?FD3*IiR13ZfHc^Q*RS6wmED7!0%WH#2cd)pMu7LWJ8p^~?a!uJ*i1dh zD_CXWslY!SO+RC^^k`X>w&4El(dKmT^3x5jGhnuAfWIOtcBs{QuDqhMFkX7|mx{vR z%e*KTzxQ5*2y|ZqK6UJtR$dh=P41!kp7wSwATVpUwcO? z^hCv0wOJSD$l|g+Pv0^AsMNmn8?dUX`nBG)ELSV0S&F4Kh9t)IQ@t1i>2E8#3Y|W4 z6ku(zDGJ>^lRcsa#soj)4-KWY5$lZ?B(m=gXq1k*oZ41oIU2(0^=ovNo&VmVv*!71 zzClZyu$QrEs#iDCV)U?q4u(*l#NH#e?L6E=<`7(E9S2E%I7FooT$mcSQmk0se{ zS!3HA*zKPBlAU)9d8G=D*R=EU$4|`PwbN|rxIUwEZXrE8k)CVu@>xuD@isU1UU=-2 z>)3dz%TJ>J_D}iPBiGBo+zhU{GEMvWapS2r%be|(OkFj#eSs@J`|p}kl-mwRvKF$j zeQN?eIJ8}S)9b}i+B|MG-m=}b<9$xwTTZ@ zgWAjcOX?rI>dO1$FPC58%1d?oOd;<-0W`7KZsP@T(R71`v-ts3;0^`XB7edEvjCEN zGX2H<<6UL{2>ga5UNh({}3x}eX&Do9~6DP$SPh1(h|cM!^U!X($GvmS_%IZG=CMN zvn;lKKfy7?4yR}--f8SKNv%`|EU9VRUMgjc(sjb!f=-Y=9>W7sXE!Zb6Ogc`0jqpO zKDR-T?PtFm4TJ;pw93RPb-et#E>;@hRrQHt{*>(=_n`JW)t-fDH{G-s<~cp zYXOt6(^t9^b*oiGt#f~I;M(P$coADxZIc=oSud8dP@h!a2_A^}Pxff97s%5x;%zrm zEhB&xh5?EK zn5U?QDAMyr0f{$V<4OjJs?%(&w(M0x&m&EQ|ew12DMG|J^xuSi@7SO6PE1hsX0i_ zxXHpmM6~cIOOZ$uzp89ScE#TqiBQ;DCYUGyEQkkG3yNRfWOcB|;8Jz~fy`xf!qVwh zzh0)$6U`Hez+sT1&Tm20JIJDN>T(&4H9DLfB5;x*96gbivZ8qx`MR7fZh#PCod8OS z=T_I&Z70EogR4y{RdtnSM-4Qv`1G07u2!PXX(-b}W)uV7B(19RWPvmr5-rsb+?nk! zMTi~R`WOiKFsU}k6`Y7$1PuJGYvVUSnCL6TLEM9m4~SO{C=F__E*(o++p%dLBg^Ev!fSnR^H&iHaB`kyLqhhxE4BHc{P|BE5yoj@D<7x)Zm^iTTC?y-tG| zo}GMfH2}mWuQ4i#p>8{Zzz?blC}w(CNQ6TyB z(6_$w^7Bx@l3sVBhrr5`Tk2x6zjFps)o}xsu&McIC`i#vPF<7N1nx1NoGQoXwU;+H zaZ<-D8Q66IC zdkVMILjb{@3Yaa4z4n^SmuGcWLIZA7yEWKwB&eOiStckFfz{dSqKFX%>Jv|u z)^dA-NDtNBe)CW$Gjp!*Vt5l+^OB?ru)~acrm%`;4oZfgz}^eCO;p(6I@DndwB5P| z&am$(18Z45)r3mU3zA7ih$@=L(JHEXgj&}t>S$m<`lGtH3eBY=mlyFF>KrSpw&uT` zHJb0huZu)LhbnT+;yDzoq#S#Bpl~rcbe5&Oi_vytKP#S%y+uB=3Ags zGpu^naxC<2eHMLG(|eqI0Mks9J!(VL#R1%XM~ll{sgI*)aE2P|>W@0Np0} z5a?rpmiKh~g1@RCRLV~;BvKkJToqc8r0ExtM?fyD+xtvtd$;vy#vjMBt0}CdU(<5u zwuQMyPc3|+F0wdnzf6O7SkD{{>_-nZ#O*a{Exe6E&v~&!_jZs3xfnzhHd`PL>KUKH zYpM}b#ZMp-guPYnYyEEi#f@my#popa5%PWj3xLRf-x!85jVKjrPI~+3?(Xeg*K`J1xPd@VBb3sw|0~R-ItIK8H~|clJj&OK(Llf3!1x% z)`N%t-q`{0HEYx?p(Y(iL7zrqr1F5`1|Q(jqP@WU-6|}N!ps1q?SYW&WYK{YVc@b2 zsBLP;2N{w)xJpC+%4H7y;)3|1BX7jmbmHvpD@lrqLbT$gxYD-Vmci>t%3|=Zp_e*{A zCF5On+CVsOdSO1rq(v#%1+gNbc5tU4PK9?hyOPo3&INZ1+o0$N58vJBuhKKYx`ZnR z26xRAuTFzeMBX!=SabT)-M6lUS^5iwoyzLJR=(LV=^r$xeLrPFP>a*LfqaA|?WPMiN6=v>IC|0L{0eO~j79yIQQA5u zMp&rVP6?+~IO};!$Nxg^dx7}<%Xr|YL^5z68~{L-=6@|w{+|QjfB)+{cryQVVU}^m z?Y71q?fSkI&N#=|HnNVGFd*jgjb+V3Jc3DP3B%?o5|(L4Y#M~sq*Gx}H~;EgsnXb8 zq-MPsBn*2=cXzjZMHZ>1ru|96)$jgzJWt9Un+*OGwyCYS(#@`|<@8MDBe%bAs>!e# z&hOq)V?Fj%B{NY(5pl7Sn8s4ut)mg#TlJin2s1TNH>)E1$Vgx}PwU_$M34E6{uP?k zh2q=d`beMZq@<2YURgmFdI`L{Z%Z60b;2aJQf1Nyq0rvpjN#+}_4Wp>0CKCjEDq!K`(4+*C{ly+zV`*<|h z;QvOl8xX-c4!}FhetDjxs%s0;yBX9I`R0*P>H{Zav-j<;oeV>JawH51A0Z?#SKFCE z%D|CYM^A~|jTO5GKhsK3NXt^cd(lw=yDbYmaRhnBTq^pd8nL}6lZt6uQ%j{~J_$>E zo?)voo@SYFh_80d7B&D4;`K&CYq5wRXp~^uZt@7j@ zFWhRSUK*nj6+$Q^1S@7p0*NHj$4|M}MI|uXWL88K&D~Jl(kZkFO{I)s!~2|^T`P@% z-lgA&#Fczz6aobNy+c@u_37v*Y`p}*O*UrG7xt1^Rx^jJuX+=Un3rFlX)GN?h~2 z7hCl5Ss28z=XOi}AVd7&aVmhjYXB8>45(jPIwm%vsgqq(uTHO12i6+(PIJ8;cdw}{ zf5ciWteJwMdssSYD|1CJUFhog;W?JE;O^+OcT=mV9rnB@K%bumi} z&Vno*Zg7k7TUI54`@mnRJV93Z3}%3Rtc0q{61JUV8tf6+kLZFl6d#*Z=mXFdFSp%R zP>L9Mp&@ATf0dH2kz%XJC2>f794$1#bvNnlpf>a1X~i#k+*Lm>h6DsoV8dWYi3Wcb zQGs<*2Esol^U%Q-Dn>#{TLqV2U2mD=vHyFOPa*C%t_c73k6J*Hzaz9C)v^Y6L7z7A#Q9 zD?lnis6p45NpRMM?nV^MBa41EhuuegIPL6sD9jAd?MDuIv6?j5-XS?!KW0^_Z?d2| zQ$$&|C1x7xRGzR5;Yr>Z|4V=wp=YO%gvlLaTCq7Ku<=XNH;2Ze4UG;PXV6VycQY&7 zoRvb?-9L--#XL8eR>nQl*xo+F8K1qfl3&!HWpfiFf}wqb7jV zw*62#yBVbEvA3e~M84kk6R94#4eK z7IzGP9Rpf?Ie1J5yjKgFrVZ@q0D zmGTG(r#)-6?j_o@`n_{TS%FBOL9O8~QPU}Z72GWI;A0A|GY3c>GoWh}$EK+y1KP&N;ct;G)Iu;771y(;g3%%wRf)i9$Wq zb3;sdM@G$W$WvfzdyRwbz;`gEkLzFyZnu6J?eLFh3VV^f*5TN~5)H@05o743@>C2c zYuoEOEPU9`w0blg`xfi8sEvwKFLEsJ$~23}o@qj}B)^6dHU!mKx4z zH*4QpPwaZq;mC$`WMeQwKkUlRk}qqLK>r|mF?e#Z65d73t>E8m(>H<$Q+#-C3u z6$!D2=%Bz(diAOe^gfzL$EWcTp9r0*BT%+&%-! zNUWNGtP4_xjT%ypLoRz+2Nilgb-(;DfN#q}-4GOFG=-xdEo>ZPyZ;l?`8Sf2kyR3{=p+c|=*gUL>X1>YiC&156maDigwNqkmaJnG9zHVfX| z9;Rb1Vbv~bW1VVP&zjQfKLR#SdHFER{C%pd#_;J=4-Akh8PFTuED9(!nOtr@kBiS5 z+lty7KM=Aq7ShAYP{#xW=^o?S$82%DvjW4>{Q)=5OfM^r)Yd(J*EN8p&oLf{Gqzs-47c(RH%sWv%KfQ z@NT3et9eYRS5&59c;|j1j!A0TJ6`IDjb@6Q)km#u1?O44kg8U zwGJ&tC;MW%3WHmXYrN~UrF5bD#v20;ZaieyAdof}lIG z)eMJp-1GW%%|BmA0H2hMb2F_TVB|>)th6&>-5cDn$BwHx;T>xO-R^2id(w*uwa)|x zdyo`)3#nLsmZ*+2GhW6=l{a1~*a{aT=3V;b?lDk0z{?luPEB*D*S9_8XbbQh>03ri zGBB#|n%^ZAvz;YWSl|SmC?`Z4&C3Xv;eOGU7X;(l9kwodCtMaTUaPh-2~~X2u!K== zRt5fL3?Cl(FAn<}5F?Z@Pw*2tW$-97Ky;(vtRJoB2G;8dRS`i!CKrzY*W=Q%5Vlk2 zYgG%1q+jjQG;^JQlC+Jw7sZ7Q!@6nD#<2dlrW}aRX_57Bd21O}ki(G$-h1Q-@v3eT zm+>1M^B7-RC>EgL{!#*5SpHsb=5&Kyij=G2L7rncLrW?2D8(^)}h2xhFlaQ~*v zHV)e97xz4Y?zkY<7t6|^PpwL7*Yx6n@@yyq&VeZ~wuTBi(#an};URBzoyn&}j+VcT zEF}a5SNsDembCJ!=EvPk?^WbROCrOU*oFb8j6q3g?{LS@oneT9zD$?w67g6;GhpWtAAPx`;Ux+^cW_& zrK(c~Fb0YQ{)|3yJv%fZ)Q#$`b-R02Wxm$!8jszJ`sQ)%azrN>l^x|XEE|NNvip8) zLs0?OFGC1KH;LJesJR@Zl8#K6dtpqW<25d6K8NH&W~B{hoNjW%?m;YX(I!N2aUsgD zfMDWxaEw1SNpT`Wa{8R)L|nu}O=k#ygWZZebl6kXiEMlil1+~?-4<^6{TJ`h@L)@K z#kXt6upM=5B$YMQvjBxDWAX*T=7&F|Lg#2@z<;;-ggVd@S|%T?!gBm}WcW#mNtM!U z3C0dkz-UCJ#ac(V&-jSBYpRJ-J6*98>$_4_Mb#T7ZNvcS>ArA@E9vK?+$;CFP|b4f znOSy>=?Ynw1-q=Lr`dMwx<*+F@lDdn+Fc7KbHaop?_T3mudQZMRm^4B^d%SAB9v&S zKki!3fMTNKAK#QRaRWQ7l-8Q+t5a`-4&&A9m+t0Sa%(rJBR)Q5u0mw!#84;TlV5pl>4AdHxM2zCM(j6ZO-MgW%pq-70m zA~>>G@cG<&my6pBLyCsn%VZx!bWL%T)WTXIVPmkpfuZKw0YK&&ou3ipQQ((rkaMCz zqxJ2xR~8p=I>@wfVvPuGp#N~nxdpCcjRPDCm~vsR$lG4MIXpNE@YY~w0TIjx34%%Z zG&fbQ)y>N&E4%luRaW!**ZyqQSn=oavaSck&MVT&ucyTZU)fa~*a(-}4mqBZcflo2 zla{^$Wj(3B)!PRUme1hr=88ZxZ)B<MQlwto`I4LhCSLnzhd zP%DE7ZxJ7MD_9*R%h1R(yIORFg7=1+Z)_A==vdu(oA!5rQQRDit5&MdM8dC3EXi!h3}djCvOf zE&_y{kZtThQ*%IPZZ&YaeHrQi;s*O%#(w7i^`BoPU6lJO&t+RiIr>BL02fxSy^IVu zjY$>0rnz$ilQkPfcPwJeepF~XICSYYpm#H;h|BEp_qF$*J9Gb80;T&ekko&4;{It| z^nV1`uUf*3}%BZD%9M>APxA--l9l@R?uLt>|IAA>55>dY0 zx6Qxi{0Y^uyb!kZa%z&W57tlYH+oQ?pV{-h}3gaFgC(-);16Cj7F-0Pos4mhXLlU>&M?r}SjuHULc$u+)YnT}re+O)5r^W# zKIBua>YcV5oFTbT%tFkDlJFlPZh}MwhjUIx=Ru{N=~Ji=5#%!fYbAu~fkRP)d}K8>k&LrIpX*nq6+46c)x zU*L^*Pk&0)-8^N3*c%gB!xqQ@>6yDgyk=VQ^f%iF6g+?m5Hur276*^3be ze8e3gC+5W(024-#(wTsO`X=mrz}Wo;LQ~rS1!BP)89*t`Xav|8xIx(m3Fr+L3AwWR zG0P49!@QLq_6ZA!K5hoyTIINDi_?!X7E)<2mmXyA2EdCc>dh-F8ZIUR&uO*BARs$I zvQ`ZnaAzhE_jScbh8|H5*p zwhHv(K(7VZl<4>uBBeC+3u8;kiln^KhCQXh?C+z%rB-3E*#{+Rv`)|^kB_Wk6nLeu zU@?rvP?KnaMuT`$Y6eI}=#OZYIwY&D2+f{|2)Cb@@rmQ?W5f^IySu=X@^d2)B(P!| zw@o&TdsdcLwp#X!c)_yTb=V;s>KJvC9K5qg5TV|SEh|m7{`1tNW}jmva(j)bg|KO~ zngJtr6pyv+x!tc_`hU+$VPGQ~^6=t3;37|TwUi$5tG7ci#-H9fj4I%jo^e1lF|<2R zz1%dWpPeAr5u)?gSzSOZgt=V8;2&~QJJ+=zcPgIMKF2j(`C6|4W+hNgg$~JGk*ydz z$jxo@buS(O$Sm?kKC~F$9cGSi7rQyoS?rWQjGM}f(<{DL-9<%_Flx%&1-qdybQNxb zywD$1bmy+`cI@S6AVFzNo-^Se$)&r2&v5&m&jIj8?t6CpJN>)91;VBMBpPG75-vUn z4I4J7v4@$fX0WKe*)U+Rge9u@J4k!KW1$nXhea_UX2%T&EkMvX%}Mxy-Kpw9aiHr4 zQl#Mw0OtP0tFkKMIU-hE#GxSI6B;~16)^6Zg-qTvh6JSOu*{bGlWkgp$*vJWo3s2z z_7hZA%>)YLIbvXlGYu|6JVDo3B#T;Z>!(Is=~HzONmm$nrKXrJYz;}#VptNCM+;~( zeT+^gV+$Y4ksK#y3N&@a(NO)OalV3dWI2t7qyqY&M-jZ!kymc3lKf34E0`vF)I+6K z$>%NCw=&J-txJo4yipOAYpHrmyg6^~Js{}4Lt(tXrFg^@ML$X~S|1-1dj@UZVWkKd z`Jr&LqHHK)mQ2202!p~fBeEty4k6-lAUwXnuVvLBrFOUdITp595J44%uo00UqB-f# zq)>TE)l3b2z5}$@AY$oYRRw(s@FO6rW>`6dHROLvlaa{uGe@yDhP?XB2(HQARJelX zb_Vlsn*WH$TD4Y92I7vY5&97v>64*3D z>8A~BC~l`&s$rlWhByHhcpGZiq7#(Vm)QsU}q9636J zepSff%E*kz1_NYhJrxXdw#oKdP9`D8hHCsUkc}sp5mfD+C39+fqL37~8EXlIe6?v zUHDM{FxPVi9UZ;A94V3Q2QP}}O+j5Dx*Jn#f)0z(X0#l3 zFL>fWy)7~IY^-MNqwpBt5~jpTIbO?Y$!dmW=5fl4WWy2#HSFyvE@nGjQfaC6$Sbd&Gh!Fle*qINQhUnSyM88Gr{-!%fd5zcn9U z4#w%(-srs1!2j0LKbFp78+>H((y>|2Go<2gr3g2_N>+TGbk?LRK7l`B!DY@Q^^>`KG|wO zLa;SyqFj8aU&#?lPXrNCgWb(eZQptLO8pD@&NGY9pHsfH85`zpwNK5{>`wzOFv3U3 zno6H-Q2E;(Phb?aE@m`S&R^&gEDDi}xn@RRumy91qFDint9COVpXKb3x5mJTcF}}V zTk;3?af=L}4*|}Z#hpPu^9$#a|8>FVFxNWlwo4SXz5Lp$Mxxz&&TOc-7J~KZaH^?c za5OMix7X17QV|8B3!a3DNTvLxZ`kUTpJ+pUxK2_vn#?Pybn%mBT^|hz;7NWT8SqgW zI}LV_YU>*xQ%#01M-19Ybk{M%z2AYrw?T3dNiMf6vN*7o;P}_g=+#H~#k;d^RV~o6 zT4oGyQ^r0Zfwg_A%@(u<5z}9L#>pIU;i{leTSUwq4|Q8!#AS7UE=6*z&P+4xibdOd zd5TztA{4ESag&MwG6d2ha$t(lw6&ZQ9#NcCic0&if+~or7G$Hi_Qi%PBV)LKqV0C> zitG{^Ny^XgHZ_Bn1{HB+0V`5IvDP$Z>GojPf>^!$%CTHf>f?&UM?z0>J%H}^Mo z+wmW-13JA@G&JaDt>|_rWz?e=kuKS(g;Aa$u@~M(EHFOCyBMW!QFDy#*uCTkhD=p%eiDg$z z2?H=N*tetT=x-K98DA`AAMK-Z)7z>k!&S;Q2 zV$_J$9{xZA$1Kx=4oLaFB3}y><7c2N0l~m^Pjw=Nes(TGU+qGj!qE)q=t2t(;@ipd zGAjHM7m31M3P5(+B9KOlw>@Eo_m!F00)%fKAXISoNUW-PQS`(GF|7^)q*ktf0Re%v z4Ls*1Qo6ygG^h>i>z)1t5j+Vuazq~@IDRjG_-eL3AveUj9w_Po0r4j_J_Lm!-)q%i zv=`}En9K;)nA7x{v{9zjrxQiH_R(OaxtZ_=^ba6TE-tTJ;}BGCij;*yURt@9iy)Y+ zG-9CAY+OhsBo#LN8c5wlZ)~d3HNCUX*Kt>Dbt0G4g6u|-!>+ReO&_&1EEzcv&y!bp z$V%O1Z8@Ila{`ZaPap9xO{=l#a|>asf_|+_1=57n^>wgr3AU!IR-ZImJGq+3^`J1U z*gSxuv5=B3$y$(>mu#Ey?D%Es zEaZpvuLgSj78|8iz=*Y9E$pv`Fz;}b#9t-t=;)qGN9yVlTO^h&r?ynQjD~Kh;#(Y& zmcM_FKAQ1m{<~~QW5)SkqUaSWa&ASDYz z{?hLm`>QJ-xGxP9hOo={_ha5p7vUbymbz1!+usgiXtZD!Ph2Pqm9KpR+h z9faj$r+1^3uiX@_{aNMh*Noj%X;l$gjvndEiB%Ek?`A7+ick(p+_ZP7m@Zw@VtCyq zeIB*_#@B$;|6B~mGIT##4q6K?Ps_pCF`tU_+n(uDKG!Da?opdIVX*V_YrQ|Up?%w4 zbe$>o)K%YY^k!bk={;n5d$Ye6?Ue7_^Om6MYi6$Y=CG*M`t!aL)RGr7dOR?G{p&ZU zndcXwkbb-?Iy3+P#{XtL{2%P$r~Upf7I0J|nx_Wrj|E(!8_-0(VAE%Y0Yp2`hON0I zF%hZVwA5mi9bc@V7k4cR>DXDuRgVb$wH2tUxEH~yo z@)7K}6p{v3ns@*_$&w&s(SVSMf(AabZ)qE8y(l?6Iay4eOa1oL}Xf zWmClXc8ig}!ucz|f+mo#!41epdOxj1;V&Za@C43`I>53KLnc zm2O}ob)Oj=NL)cGW~bMYR3+}7cD_EO1Pr*|B9*Dfrg~w)WSf%lR#OooZI`4j!5iL=%c*(eDe zLMxNad?>-uo9rXzwKB9E4b4!hUhsFNpxl*IjWU@e_gPr?lFJ}!35C9%YMvG*)rHi`X^1$k@$Yx-XmW%FCc9D#==FHFG zWJNc%cD3|&HQyZ_po-O=^JUJdCS?4|!_KL80Ho}gP$ab$fsQp44LwHDqSbJCNK=k^e*E!|_3`!Sq(*b{6cg+jJTN%= z!8t;x>WTr?$#Za0sqg^YDiCk$GtS=K9Ss9oXoC8mnE0{+z#kJvAWm}M0wHOil4T55 zgJQY-#(iA6Ld{vw)Q^hF47+gv;Q#6DJb;?mzCRv%M--7FAV_Z^Nbe%O_a-$|kseq)P`yMY@1=0a4(;yf?gHe&BoaznPs)GP9p^_THQ9=H#C9 z{m!DV*Dn}4oV7%q%v$Y0^zorH>G`*No4#SuSr}z&8Ut3VN0ugB3YEIPFWUC>I*i<%1V0O^hU9^= zQcvm%o6sVNMJ>H$`7kWzL%i6ol8QWfIxJvfEK(_O{c^LALFsI=OMz$L8I`(;;DT@* zBRV(`ZE1lqNIXyA&MhcvUTf@H3uu^DZ_jnS{zmS)^^c%RGbI)?Ol|Ovd$jMFgDPDmZiy7c5Tj0muoXFzP*%_2R=hIuxa zl5i=KpD;(nV<366^HIvj*L+&2c`8|IUX-*Bnkcoq{$Cgbdb zdZ!5B)T;9l?)(nzEp>}o5|L?VKE~8!kTrl;cgx0Gw_!gSnDwemFd3|F2)5tM{kGMC z&)hI}lP|68?cO~zD9fx}6H*e(f^8gkmSr^YW`De6-Mi5372^*gS=XrWSmTz~SXC4; z`Xza^nhGBB6XJ}CuH{xovIgN~1Sy^wrR+Cmk8r;sZ6^AyKWTG#K?7WQ#<+}sI|4-A zI{l2@81j9*VJjxgaV#}~7FHDeBmYA5$d9p#wGngtp5C6eJIK9_3d*U#QaoV&GoYfo zKzKpyLb$Dmh)5)F(0_Folz%~vZo;(iDb}qbf^18A*%36BLni^nt4^Lf#)owL#2XBui zm$qv$Cy$E0>(-i{mS&f_p`q(DcI9rap03`h$w|y(LgL4X1hUx1DA@LGO? zDJNS`s14K;3cl&#;;gTws@Vh~-sjTSU?|Iz&=7~WVk4*+MoF?cRqKTclsVgQ6ow9B zE1a!NFSliWTj?X(VO{ORz0@MGC|{Q=wlfam_z=xQC&8iqbz_*wmDrUdzP7L=V~EOX zRI4PE+&*QsL*`l%9zz-q42#q1o zOr`wG7#(-7a~eiMm9Qy%EWZ@T-7krW(F+;^se9>6C&`^it`;y`FIyPc)5lY_SmhaeBruF~=p*-I zK)e)sIAXv?eYihim~q$aR^?IQZs#r6QBrB= z21oMZap=5*yyF*o$Nz46hOncTx9>amFbO-4N_V?jxehxAr~1qcp|V=F4ts&XaCc{~ z>X3R@(e%vZ47y)G-`Hag0RBi~0RNbr;+ML-o{XG~o{a5%nZwBxzVd@2QDiCJjzL3| zPn}%tW-Y``_}PfU&B+comWdGrNET6JlqfCzYA*IXo?c}sv#9I3>j*>w5L^w@k0nz%HI^jP&h!mDLo+M6$~61LmRql%ON&@JJ-i zjBpe36g(y_jGHlBoi{h%FhuHDwMWZs<~jD1toE+4vT5R{q4YX^w!}qK|HN>ds_T1N zs%M)__i2{f?)VrNJLi!E)|$t8wjub04?a8If6gJ5R`jd&c|q{O&z(HiOLm)_~#V1>G}?^o%zm0nFn`;s)ck!SjPD(klhaSooZR^=Un_W z615O>H)2j{Rxu8WfWO20g%QZyA`mL?|21gJ$KJkkRz_&+(v$#}F8yVZwM-kaGK$fe zZ8670*!}1m`2*yBXPt{WVC_=<8{#DPi78h>xO+b>zFe&AxkME!3Xx6`p)p2iW>Ie8 zzlaDUGs|sBlqse#Xi3_qVKxs~p(zzx1rZ`vvV~sp!$?7&Zu`gknE(8`vG80@!++0J zi=F1>CC#kzf~PATF@ksN#gp%C0Y6>FJUVD=rpuEt3p<7N ziwg_j5@wJJ-`A#dpw{m{*oVf&OhCVkw}3*ep?Le%4D9c%NJH)i{UoU1OL6kUF=5NO zvhg(x-cU{19jsral&Y9f`StB4m);Z~DCpM&4&{ja`<_OY=sD-{M+I_L$e%G3E)N&S zL#qiYJ&FyG3VBSUmn)+^Cb zibLzStWv?|Nmh2>jLw{ovEnV3qHVJC%gG*hKpMhCif`H_-wFSK*H02UY)Q%gt6JN-a}Z5Tptl$K`VZy5eco4^{b8ujsF;Jkg@C=#(I{)E(2R} zjswJ+orJmXvn?dGGpevOs;C|ij=LB)<>*%RG2+q0H2pR0yo={97IU)Eii097IH|nV z$&u|FnbC!u!4e^78Hf$A{5mLw?=m2?wLH&T-DAbce(`lNR?h|mciX}7&X_In4Vhuh zuo3p6xMNfM-B^Q?p{%GLW8jTGBBIas-7|qg1}aZbo?Dz>UiVi4&6SWEae+8Pzn3pX z>(!I=Xs#PAgnsw3<}NZTSuPM=)?TjwR&QhPa#fH@-#=j9-C)75;7FdVBx%d>ci#*% z{Y1BJBquxM`1Wpf5m8JW^-8ITF*#P$N6BWUQX*O`NNo0JrPQU2X4fU`99jMv*0swD z`RiJdX1$clM7*k)Zs?(iM`wwTMdfcLkwU~e4h%diaeQ4I^3VyNG{+uAI@Ilt#LQ$(@b^H~^ z0EV)u$`v6Sir#c$@cP`bt1U~%yZhlx_Qct6?!7GAuSuLk`sx3%w z{tC&0zm?)Q4^v4jkxhN7G_dkwb;1+N$1?L5J{k=TGmyRMLUbC1%~%$*4D}neDRjNH z?Sftb=~At~oQi_rI`{{U*=G&r(x#^H68ehLjieC`tPf6IF8p}~Pp|+;-O8V?dA$1C zT4&<(hugHFL9&_E&Cz*p_|H00zgl4#pA3v}T_-)ky6bUhFU6_=N8JK8y5A_vfM+Pm-_L%# zoj&dmK8}-K%@aqgA)p)Wf%LN9$H(LBag?=-6QFiwgYJz0aL>UYX2|;lu1rA9#uK#`>0rr;8KX=^S0SVV)J`VUFRpH|&gATfUL;?0lI1x}Q66ML? zp#RnQJMF*qHX`;aKzz;kW z7)?XN!ok_W)579}u|ZdDL;-`i{{TdEH)R1sljXxt%|4f{A(L4NxKraG`;^%Arcm9d10?|hpz0eBEREg<-H-$Q_6F!OP z+4Cq964s}iI7+2F{o4sWIU2>Svi*1NA12X1snY13g`NhAvMA~B?-qYc3PndBpHhd* zYL6NcuHS_r-~Yz`Pg4o~pE{J8n!Eqa4C-{yrqM}pN)(>@0f0Xt zAcYpSJl^FeMI%wbuv37i-b|eoZA0NU9-i#qZ?QLY@6aP|D4bN>$+*AbZ|E5G02vB1 qd