From a808ed9926c020695fc3eb7bcf9181df8e3655f3 Mon Sep 17 00:00:00 2001 From: Paymaun Date: Thu, 28 Jan 2021 12:50:10 -0800 Subject: [PATCH] Digital twins staging to dev (#305) * ADT CLI features and functionality around managed identity. (#295) * ADT CLI features and functionality around private endpoints + links. (#301) * Includes significant DT CLI IT tooling upgrades. * Simplify DT client init. (#304) * Debug logging improvements. * Update HISTORY.rst --- HISTORY.rst | 14 +- azext_iot/_factory.py | 6 +- azext_iot/common/embedded_cli.py | 14 +- azext_iot/digitaltwins/_help.py | 153 ++++++- azext_iot/digitaltwins/command_map.py | 35 +- azext_iot/digitaltwins/commands_resource.py | 196 ++++----- azext_iot/digitaltwins/common.py | 31 +- azext_iot/digitaltwins/params.py | 115 +++++- azext_iot/digitaltwins/providers/__init__.py | 10 +- azext_iot/digitaltwins/providers/base.py | 26 +- .../providers/endpoint/__init__.py | 5 + .../providers/endpoint/builders.py | 330 +++++++++++++++ azext_iot/digitaltwins/providers/rbac.py | 11 + azext_iot/digitaltwins/providers/resource.py | 317 ++++++++++----- azext_iot/digitaltwins/providers/twin.py | 4 +- .../azure_digital_twins_management_client.py | 20 +- .../controlplane/models/__init__.py | 52 +++ ...e_digital_twins_management_client_enums.py | 35 ++ .../controlplane/models/check_name_result.py | 2 +- .../models/check_name_result_py3.py | 2 +- .../models/connection_properties.py | 52 +++ .../connection_properties_private_endpoint.py | 34 ++ ...nection_properties_private_endpoint_py3.py | 34 ++ ...s_private_link_service_connection_state.py | 44 ++ ...ivate_link_service_connection_state_py3.py | 44 ++ .../models/connection_properties_py3.py | 52 +++ .../controlplane/models/connection_state.py | 47 +++ .../models/connection_state_py3.py | 47 +++ .../models/digital_twins_description.py | 21 +- .../models/digital_twins_description_paged.py | 2 +- .../models/digital_twins_description_py3.py | 25 +- .../models/digital_twins_endpoint_resource.py | 2 +- .../digital_twins_endpoint_resource_paged.py | 2 +- ...ital_twins_endpoint_resource_properties.py | 17 +- ..._twins_endpoint_resource_properties_py3.py | 19 +- .../digital_twins_endpoint_resource_py3.py | 2 +- .../models/digital_twins_identity.py | 50 +++ .../models/digital_twins_identity_py3.py | 50 +++ .../models/digital_twins_patch_description.py | 10 +- .../digital_twins_patch_description_py3.py | 12 +- .../models/digital_twins_patch_properties.py | 30 ++ .../digital_twins_patch_properties_py3.py | 30 ++ .../models/digital_twins_resource.py | 4 + .../models/digital_twins_resource_py3.py | 6 +- .../controlplane/models/error_definition.py | 2 +- .../models/error_definition_py3.py | 2 +- .../controlplane/models/error_response.py | 2 +- .../controlplane/models/error_response_py3.py | 2 +- .../controlplane/models/event_grid.py | 15 +- .../controlplane/models/event_grid_py3.py | 19 +- .../controlplane/models/event_hub.py | 32 +- .../controlplane/models/event_hub_py3.py | 36 +- .../models/group_id_information.py | 51 +++ .../models/group_id_information_properties.py | 37 ++ .../group_id_information_properties_model.py | 34 ++ ...oup_id_information_properties_model_py3.py | 34 ++ .../group_id_information_properties_py3.py | 37 ++ .../models/group_id_information_py3.py | 51 +++ .../models/group_id_information_response.py | 29 ++ .../group_id_information_response_py3.py | 29 ++ .../controlplane/models/operation.py | 2 +- .../controlplane/models/operation_paged.py | 2 +- .../controlplane/models/operation_py3.py | 2 +- .../controlplane/models/private_endpoint.py | 35 ++ .../models/private_endpoint_connection.py | 52 +++ .../private_endpoint_connection_properties.py | 48 +++ ...vate_endpoint_connection_properties_py3.py | 48 +++ .../models/private_endpoint_connection_py3.py | 52 +++ .../private_endpoint_connections_response.py | 29 ++ ...ivate_endpoint_connections_response_py3.py | 29 ++ .../models/private_endpoint_py3.py | 35 ++ .../controlplane/models/service_bus.py | 32 +- .../controlplane/models/service_bus_py3.py | 36 +- .../controlplane/operations/__init__.py | 4 + .../digital_twins_endpoint_operations.py | 36 +- .../operations/digital_twins_operations.py | 141 ++++--- .../controlplane/operations/operations.py | 8 +- ...private_endpoint_connections_operations.py | 364 +++++++++++++++++ .../private_link_resources_operations.py | 164 ++++++++ .../sdk/digitaltwins/controlplane/version.py | 2 +- .../dataplane/azure_digital_twins_api.py | 3 - .../service/provisioning_service_client.py | 2 - .../device/iot_hub_gateway_device_ap_is.py | 2 - .../service/iot_hub_gateway_service_ap_is.py | 2 - azext_iot/tests/digitaltwins/__init__.py | 140 +++++-- .../test_dt_model_lifecycle_int.py | 39 +- .../test_dt_privatelinks_lifecycle_int.py | 176 ++++++++ .../test_dt_resource_lifecycle_int.py | 380 ++++++++++++------ .../test_dt_twin_lifecycle_int.py | 51 ++- pytest.ini.example | 1 + 90 files changed, 3747 insertions(+), 596 deletions(-) create mode 100644 azext_iot/digitaltwins/providers/endpoint/__init__.py create mode 100644 azext_iot/digitaltwins/providers/endpoint/builders.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/connection_properties.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_endpoint.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_endpoint_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_link_service_connection_state.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_link_service_connection_state_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/connection_state.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/connection_state_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_identity.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_identity_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_properties.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_properties_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/group_id_information.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_model.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_model_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_response.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_response_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_properties.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_properties_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connections_response.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connections_response_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_py3.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/operations/private_endpoint_connections_operations.py create mode 100644 azext_iot/sdk/digitaltwins/controlplane/operations/private_link_resources_operations.py create mode 100644 azext_iot/tests/digitaltwins/test_dt_privatelinks_lifecycle_int.py diff --git a/HISTORY.rst b/HISTORY.rst index d0237e16c..79f82ed08 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,10 +6,22 @@ Release History 0.10.9 +++++++++++++++ -**Azure IoT Product Certification service** +**Azure IoT Product Certification service updates** * Fix bug for `az iot product test create` sending a byte string instead of "regular" base64 string. +**Azure Digital Twins updates** + +* Addition of Digital Twins Identity support focused around Managed Service Identity (MSI) and Identity based endpoint integration. +* Addition of Digital Twins networking functionality around private-links and private-endpoint connections. See "az dt network". + +**IoT Hub updates** + +* Improve http debug logging. +* Fix bug related to issue #296. Adds a clause to device-identity update that allows user to update primary-key / secondary-key +and primary-thumbprint / secondary-thumbprint values (respectively, per auth method) without needing to specify the auth_method in the update command. + + 0.10.8 +++++++++++++++ diff --git a/azext_iot/_factory.py b/azext_iot/_factory.py index 9915c410e..98fcbbf44 100644 --- a/azext_iot/_factory.py +++ b/azext_iot/_factory.py @@ -10,6 +10,7 @@ from azext_iot.common.sas_token_auth import SasTokenAuthentication from azext_iot.common.shared import SdkType +from azext_iot.constants import USER_AGENT from msrestazure.azure_exceptions import CloudError __all__ = [ @@ -70,7 +71,10 @@ def __init__(self, target, device_id=None, auth_override=None): def get_sdk(self, sdk_type): sdk_map = self._construct_sdk_map() - return sdk_map[sdk_type]() + sdk_client = sdk_map[sdk_type]() + sdk_client.config.enable_http_logger = True + sdk_client.config.add_user_agent(USER_AGENT) + return sdk_client def _construct_sdk_map(self): return { diff --git a/azext_iot/common/embedded_cli.py b/azext_iot/common/embedded_cli.py index 2fa7a8826..5e5ab471a 100644 --- a/azext_iot/common/embedded_cli.py +++ b/azext_iot/common/embedded_cli.py @@ -23,14 +23,22 @@ def __init__(self): def invoke(self, command: str, subscription: str = None): output_file = StringIO() + command = self._ensure_json_output(command=command) if subscription: command = self._ensure_subscription( command=command, subscription=subscription ) - self.error_code = ( - self.az_cli.invoke(shlex.split(command), out_file=output_file) or 0 - ) + + # TODO: Capture stderr? + try: + self.error_code = ( + self.az_cli.invoke(shlex.split(command), out_file=output_file) or 0 + ) + except SystemExit as se: + # Support caller error handling + self.error_code = se.code + self.output = output_file.getvalue() logger.debug( "Embedded CLI received error code: %s, output: '%s'", diff --git a/azext_iot/digitaltwins/_help.py b/azext_iot/digitaltwins/_help.py index 67fc1a77a..10dd5c1ae 100644 --- a/azext_iot/digitaltwins/_help.py +++ b/azext_iot/digitaltwins/_help.py @@ -22,13 +22,34 @@ def load_digitaltwins_help(): short-summary: Create a new Digital Twins instance. examples: - - name: Create instance in target resource group with default location. + - name: Create instance in target resource group using the resource group location. text: > - az dt create -n {instance_name} -g {resouce_group} -l eastus2euap + az dt create -n {instance_name} -g {resouce_group} - name: Create instance in target resource group with specified location and tags. text: > - az dt create -n {instance_name} -g {resouce_group} -l westcentralus --tags a=b c=d + az dt create -n {instance_name} -g {resouce_group} -l westus --tags a=b c=d + + - name: Create instance in the target resource group with a system managed identity. + text: > + az dt create -n {instance_name} -g {resouce_group} --assign-identity + + - name: Create instance in the target resource group with a system managed identity then + assign the identity to one or more scopes (space-separated) with the role of Contributor. + text: > + az dt create -n {instance_name} -g {resouce_group} --assign-identity + --scopes + "/subscriptions/a12345ea-bb21-994d-2263-c716348e32a1/resourceGroups/ProResourceGroup/providers/Microsoft.EventHub/namespaces/myEventHubNamespace/eventhubs/myEventHub" + "/subscriptions/a12345ea-bb21-994d-2263-c716348e32a1/resourceGroups/ProResourceGroup/providers/Microsoft.ServiceBus/namespaces/myServiceBusNamespace/topics/myTopic" + + - name: Create instance in the target resource group with a system managed identity then + assign the identity to one or more scopes with a custom specified role. + text: > + az dt create -n {instance_name} -g {resouce_group} --assign-identity + --scopes + "/subscriptions/a12345ea-bb21-994d-2263-c716348e32a1/resourceGroups/ProResourceGroup/providers/Microsoft.EventHub/namespaces/myEventHubNamespace/eventhubs/myEventHub" + "/subscriptions/a12345ea-bb21-994d-2263-c716348e32a1/resourceGroups/ProResourceGroup/providers/Microsoft.ServiceBus/namespaces/myServiceBusNamespace/topics/myTopic" + --role MyCustomRole """ helps["dt show"] = """ @@ -72,9 +93,12 @@ def load_digitaltwins_help(): short-summary: Delete an existing Digital Twins instance. examples: - - name: Delete an arbitrary instance. + - name: Delete an arbitrary instance in blocking fashion with a confirmation prompt. text: > az dt delete -n {instance_name} + - name: Delete an arbitrary instance with no blocking or prompt. + text: > + az dt delete -n {instance_name} -y --no-wait """ helps["dt endpoint"] = """ @@ -104,10 +128,11 @@ def load_digitaltwins_help(): helps["dt endpoint create eventhub"] = """ type: command short-summary: Adds an EventHub endpoint to a Digital Twins instance. - Requires pre-created resource. + Requires pre-created resource. The instance must be created + with a managed identity to support identity based endpoint integration examples: - - name: Adds an EventHub endpoint to a target instance. + - name: Adds an EventHub endpoint to a target instance using Key based auth. text: > az dt endpoint create eventhub --endpoint-name {endpoint_name} --eventhub-resource-group {eventhub_resource_group} @@ -115,15 +140,25 @@ def load_digitaltwins_help(): --eventhub {eventhub_name} --eventhub-policy {eventhub_policy} -n {instance_name} + + - name: Adds an EventHub endpoint to a target instance using Identity based auth. + text: > + az dt endpoint create eventhub --endpoint-name {endpoint_name} + --eventhub-resource-group {eventhub_resource_group} + --eventhub-namespace {eventhub_namespace} + --eventhub {eventhub_name} + --auth-type IdentityBased + -n {instance_name} """ helps["dt endpoint create servicebus"] = """ type: command short-summary: Adds a ServiceBus Topic endpoint to a Digital Twins instance. - Requires pre-created resource. + Requires pre-created resource. The instance must be created + with a managed identity to support identity based endpoint integration examples: - - name: Adds a ServiceBus Topic endpoint to a target instance. + - name: Adds a ServiceBus Topic endpoint to a target instance using Key based auth. text: > az dt endpoint create servicebus --endpoint-name {endpoint_name} --servicebus-resource-group {servicebus_resource_group} @@ -131,6 +166,14 @@ def load_digitaltwins_help(): --servicebus-topic {servicebus_topic_name} --servicebus-policy {servicebus_policy} -n {instance_name} + + - name: Adds a ServiceBus Topic endpoint to a target instance using Identity based auth. + text: > + az dt endpoint create servicebus --endpoint-name {endpoint_name} + --servicebus-resource-group {servicebus_resource_group} + --servicebus-namespace {servicebus_namespace} + --servicebus-topic {servicebus_topic_name} + -n {instance_name} """ helps["dt endpoint list"] = """ @@ -158,9 +201,101 @@ def load_digitaltwins_help(): short-summary: Remove an endpoint from a Digital Twins instance. examples: - - name: Remove an endpoint from an instance. + - name: Remove an endpoint from an instance and block until the operation is complete. text: > az dt endpoint delete -n {instance_name} --endpoint-name {endpoint_name} + - name: Remove an endpoint from an instance without confirmation or blocking. + text: > + az dt endpoint delete -n {instance_name} --endpoint-name {endpoint_name} -y --no-wait + """ + + helps["dt network"] = """ + type: group + short-summary: Manage Digital Twins network configuration including private links and endpoint connections. + """ + + helps["dt network private-link"] = """ + type: group + short-summary: Manage Digital Twins instance private-link operations. + """ + + helps["dt network private-link show"] = """ + type: command + short-summary: Show a private-link associated with the instance. + + examples: + - name: Show the private-link named "API" associated with the instance. + text: > + az dt network private-link show -n {instance_name} --link-name API + """ + + helps["dt network private-link list"] = """ + type: command + short-summary: List private-links associated with the Digital Twins instance. + + examples: + - name: List all private-links associated with the instance. + text: > + az dt network private-link list -n {instance_name} + """ + + helps["dt network private-endpoint"] = """ + type: group + short-summary: Manage Digital Twins instance private-endpoints. + long-summary: Use 'az network private-endpoint create' to create a private-endpoint and link to a Digital Twins resource. + """ + + helps["dt network private-endpoint connection"] = """ + type: group + short-summary: Manage Digital Twins instance private-endpoint connections. + """ + + helps["dt network private-endpoint connection list"] = """ + type: command + short-summary: List private-endpoint connections associated with the Digital Twins instance. + + examples: + - name: List all private-endpoint connections associated with the instance. + text: > + az dt network private-endpoint connection list -n {instance_name} + """ + + helps["dt network private-endpoint connection show"] = """ + type: command + short-summary: Show a private-endpoint connection associated with the Digital Twins instance. + + examples: + - name: Show details of the private-endpoint connection named ba8408b6-1372-41b2-aef8-af43afc4729f. + text: > + az dt network private-endpoint connection show -n {instance_name} --cn ba8408b6-1372-41b2-aef8-af43afc4729f + """ + + helps["dt network private-endpoint connection set"] = """ + type: command + short-summary: Set the state of a private-endpoint connection associated with the Digital Twins instance. + + examples: + - name: Approve a pending private-endpoint connection associated with the instance and add a description. + text: > + az dt network private-endpoint connection set -n {instance_name} --cn {connection_name} --status Approved --desc "A description." + + - name: Reject a private-endpoint connection associated with the instance and add a description. + text: > + az dt network private-endpoint connection set -n {instance_name} --cn {connection_name} --status Rejected --desc "Does not comply." + """ + + helps["dt network private-endpoint connection delete"] = """ + type: command + short-summary: Delete a private-endpoint connection associated with the Digital Twins instance. + + examples: + - name: Delete the private-endpoint connection named ba8408b6-1372-41b2-aef8-af43afc4729f with confirmation. Block until finished. + text: > + az dt network private-endpoint connection delete -n {instance_name} --cn ba8408b6-1372-41b2-aef8-af43afc4729f + + - name: Delete the private-endpoint connection named ba8408b6-1372-41b2-aef8-af43afc4729f no confirmation. Return immediately. + text: > + az dt network private-endpoint connection delete -n {instance_name} --cn ba8408b6-1372-41b2-aef8-af43afc4729f -y --no-wait """ helps["dt role-assignment"] = """ diff --git a/azext_iot/digitaltwins/command_map.py b/azext_iot/digitaltwins/command_map.py index c2cc25020..3b7df0b27 100644 --- a/azext_iot/digitaltwins/command_map.py +++ b/azext_iot/digitaltwins/command_map.py @@ -4,8 +4,6 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -from azure.cli.core.profiles import ResourceType - """ Load CLI commands """ @@ -39,12 +37,11 @@ def load_digitaltwins_commands(self, _): with self.command_group( "dt", command_type=digitaltwins_resource_ops, - resource_type=ResourceType.MGMT_RESOURCE_RESOURCES, ) as cmd_group: cmd_group.command("create", "create_instance") cmd_group.show_command("show", "show_instance") cmd_group.command("list", "list_instances") - cmd_group.command("delete", "delete_instance") + cmd_group.command("delete", "delete_instance", confirmation=True, supports_no_wait=True) with self.command_group( "dt endpoint", command_type=digitaltwins_resource_ops @@ -65,7 +62,7 @@ def load_digitaltwins_commands(self, _): "ProvisioningState:properties.provisioningState,CreatedTime:properties.createdTime}" ), ) - cmd_group.command("delete", "delete_endpoint") + cmd_group.command("delete", "delete_endpoint", confirmation=True, supports_no_wait=True) with self.command_group( "dt endpoint create", command_type=digitaltwins_resource_ops @@ -140,3 +137,31 @@ def load_digitaltwins_commands(self, _): ) cmd_group.command("update", "update_model") cmd_group.command("delete", "delete_model") + + with self.command_group( + "dt network", + command_type=digitaltwins_resource_ops, + ) as cmd_group: + pass + + with self.command_group( + "dt network private-link", + command_type=digitaltwins_resource_ops, + ) as cmd_group: + cmd_group.show_command("show", "show_private_link") + cmd_group.command("list", "list_private_links") + + with self.command_group( + "dt network private-endpoint", + command_type=digitaltwins_resource_ops, + ) as cmd_group: + pass + + with self.command_group( + "dt network private-endpoint connection", + command_type=digitaltwins_resource_ops, + ) as cmd_group: + cmd_group.command("set", "set_private_endpoint_conn") + cmd_group.show_command("show", "show_private_endpoint_conn") + cmd_group.command("list", "list_private_endpoint_conns") + cmd_group.command("delete", "delete_private_endpoint_conn", confirmation=True, supports_no_wait=True) diff --git a/azext_iot/digitaltwins/commands_resource.py b/azext_iot/digitaltwins/commands_resource.py index aa1b0b58b..96d176bc0 100644 --- a/azext_iot/digitaltwins/commands_resource.py +++ b/azext_iot/digitaltwins/commands_resource.py @@ -5,16 +5,37 @@ # -------------------------------------------------------------------------------------------- from azext_iot.digitaltwins.providers.resource import ResourceProvider -from azext_iot.digitaltwins.common import ADTEndpointType +from azext_iot.digitaltwins.common import ( + ADTEndpointType, + ADTEndpointAuthType, + ADTPublicNetworkAccessType, +) from knack.log import get_logger logger = get_logger(__name__) -def create_instance(cmd, name, resource_group_name, location=None, tags=None): +def create_instance( + cmd, + name, + resource_group_name, + location=None, + tags=None, + assign_identity=None, + scopes=None, + role_type="Contributor", + public_network_access=ADTPublicNetworkAccessType.enabled.value, +): rp = ResourceProvider(cmd) return rp.create( - name=name, resource_group_name=resource_group_name, location=location, tags=tags + name=name, + resource_group_name=resource_group_name, + location=location, + tags=tags, + assign_identity=assign_identity, + scopes=scopes, + role_type=role_type, + public_network_access=public_network_access, ) @@ -63,44 +84,22 @@ def add_endpoint_eventgrid( eventgrid_resource_group, resource_group_name=None, endpoint_subscription=None, - dead_letter_endpoint=None, - tags=None, -): - return _add_endpoint_eventgrid( - cmd=cmd, - name=name, - endpoint_name=endpoint_name, - eventgrid_resource_group=eventgrid_resource_group, - eventgrid_topic_name=eventgrid_topic_name, - resource_group_name=resource_group_name, - endpoint_subscription=endpoint_subscription, - dead_letter_endpoint=dead_letter_endpoint, - tags=tags, - ) - - -def _add_endpoint_eventgrid( - cmd, - name, - endpoint_name, - eventgrid_topic_name, - eventgrid_resource_group, - resource_group_name=None, - endpoint_subscription=None, - dead_letter_endpoint=None, - tags=None, + dead_letter_uri=None, + dead_letter_secret=None, + auth_type=ADTEndpointAuthType.keybased.value, ): rp = ResourceProvider(cmd) return rp.add_endpoint( name=name, resource_group_name=resource_group_name, endpoint_name=endpoint_name, - endpoint_resource_type=ADTEndpointType.eventgridtopic, + endpoint_resource_type=ADTEndpointType.eventgridtopic.value, endpoint_resource_name=eventgrid_topic_name, endpoint_resource_group=eventgrid_resource_group, endpoint_subscription=endpoint_subscription, - dead_letter_endpoint=dead_letter_endpoint, - tags=tags, + dead_letter_uri=dead_letter_uri, + dead_letter_secret=dead_letter_secret, + auth_type=auth_type, ) @@ -110,54 +109,28 @@ def add_endpoint_servicebus( endpoint_name, servicebus_topic_name, servicebus_resource_group, - servicebus_policy, - servicebus_namespace, - resource_group_name=None, - endpoint_subscription=None, - dead_letter_endpoint=None, - tags=None, -): - return _add_endpoint_servicebus( - cmd=cmd, - name=name, - endpoint_name=endpoint_name, - servicebus_topic_name=servicebus_topic_name, - servicebus_resource_group=servicebus_resource_group, - servicebus_policy=servicebus_policy, - servicebus_namespace=servicebus_namespace, - resource_group_name=resource_group_name, - endpoint_subscription=endpoint_subscription, - dead_letter_endpoint=dead_letter_endpoint, - tags=tags, - ) - - -def _add_endpoint_servicebus( - cmd, - name, - endpoint_name, - servicebus_topic_name, - servicebus_resource_group, - servicebus_policy, servicebus_namespace, + servicebus_policy=None, resource_group_name=None, endpoint_subscription=None, - dead_letter_endpoint=None, - tags=None, + dead_letter_uri=None, + dead_letter_secret=None, + auth_type=ADTEndpointAuthType.keybased.value, ): rp = ResourceProvider(cmd) return rp.add_endpoint( name=name, resource_group_name=resource_group_name, endpoint_name=endpoint_name, - endpoint_resource_type=ADTEndpointType.servicebus, + endpoint_resource_type=ADTEndpointType.servicebus.value, endpoint_resource_name=servicebus_topic_name, endpoint_resource_group=servicebus_resource_group, endpoint_resource_namespace=servicebus_namespace, endpoint_resource_policy=servicebus_policy, endpoint_subscription=endpoint_subscription, - dead_letter_endpoint=dead_letter_endpoint, - tags=tags, + dead_letter_uri=dead_letter_uri, + dead_letter_secret=dead_letter_secret, + auth_type=auth_type, ) @@ -167,52 +140,81 @@ def add_endpoint_eventhub( endpoint_name, eventhub_name, eventhub_resource_group, - eventhub_policy, eventhub_namespace, + eventhub_policy=None, resource_group_name=None, endpoint_subscription=None, - dead_letter_endpoint=None, - tags=None, + dead_letter_uri=None, + dead_letter_secret=None, + auth_type=ADTEndpointAuthType.keybased.value, ): - return _add_endpoint_eventhub( - cmd=cmd, + rp = ResourceProvider(cmd) + return rp.add_endpoint( name=name, - endpoint_name=endpoint_name, - eventhub_name=eventhub_name, - eventhub_resource_group=eventhub_resource_group, - eventhub_policy=eventhub_policy, - eventhub_namespace=eventhub_namespace, resource_group_name=resource_group_name, + endpoint_name=endpoint_name, + endpoint_resource_type=ADTEndpointType.eventhub.value, + endpoint_resource_name=eventhub_name, + endpoint_resource_group=eventhub_resource_group, + endpoint_resource_namespace=eventhub_namespace, + endpoint_resource_policy=eventhub_policy, endpoint_subscription=endpoint_subscription, - dead_letter_endpoint=dead_letter_endpoint, - tags=tags, + dead_letter_uri=dead_letter_uri, + dead_letter_secret=dead_letter_secret, + auth_type=auth_type, ) -def _add_endpoint_eventhub( +def show_private_link(cmd, name, link_name, resource_group_name=None): + rp = ResourceProvider(cmd) + return rp.get_private_link( + name=name, resource_group_name=resource_group_name, link_name=link_name + ) + + +def list_private_links(cmd, name, resource_group_name=None): + rp = ResourceProvider(cmd) + return rp.list_private_links(name=name, resource_group_name=resource_group_name) + + +def set_private_endpoint_conn( cmd, name, - endpoint_name, - eventhub_name, - eventhub_resource_group, - eventhub_policy, - eventhub_namespace, + conn_name, + status, + description=None, + group_ids=None, + actions_required=None, resource_group_name=None, - endpoint_subscription=None, - dead_letter_endpoint=None, - tags=None, ): rp = ResourceProvider(cmd) - return rp.add_endpoint( + return rp.set_private_endpoint_conn( name=name, resource_group_name=resource_group_name, - endpoint_name=endpoint_name, - endpoint_resource_type=ADTEndpointType.eventhub, - endpoint_resource_name=eventhub_name, - endpoint_resource_group=eventhub_resource_group, - endpoint_resource_namespace=eventhub_namespace, - endpoint_resource_policy=eventhub_policy, - endpoint_subscription=endpoint_subscription, - dead_letter_endpoint=dead_letter_endpoint, - tags=tags, + conn_name=conn_name, + status=status, + description=description, + group_ids=group_ids, + actions_required=actions_required, + ) + + +def show_private_endpoint_conn(cmd, name, conn_name, resource_group_name=None): + rp = ResourceProvider(cmd) + return rp.get_private_endpoint_conn( + name=name, resource_group_name=resource_group_name, conn_name=conn_name + ) + + +def list_private_endpoint_conns(cmd, name, resource_group_name=None): + rp = ResourceProvider(cmd) + return rp.list_private_endpoint_conns( + name=name, resource_group_name=resource_group_name + ) + + +def delete_private_endpoint_conn(cmd, name, conn_name, resource_group_name=None): + rp = ResourceProvider(cmd) + return rp.delete_private_endpoint_conn( + name=name, resource_group_name=resource_group_name, conn_name=conn_name ) diff --git a/azext_iot/digitaltwins/common.py b/azext_iot/digitaltwins/common.py index aeb99af92..b0a382a70 100644 --- a/azext_iot/digitaltwins/common.py +++ b/azext_iot/digitaltwins/common.py @@ -14,9 +14,38 @@ class ADTEndpointType(Enum): """ - ADT Location Type. + ADT endpoint type. """ eventgridtopic = "eventgridtopic" servicebus = "servicebus" eventhub = "eventhub" + + +class ADTEndpointAuthType(Enum): + """ + ADT endpoint auth type. + """ + + identitybased = "IdentityBased" + keybased = "KeyBased" + + +class ADTPrivateConnectionStatusType(Enum): + """ + ADT private endpoint connection status type. + """ + + pending = "Pending" + approved = "Approved" + rejected = "Rejected" + disconnected = "Disconnected" + + +class ADTPublicNetworkAccessType(Enum): + """ + ADT private endpoint connection status type. + """ + + enabled = "Enabled" + disabled = "Disabled" diff --git a/azext_iot/digitaltwins/params.py b/azext_iot/digitaltwins/params.py index 90a1edfc4..f9a217107 100644 --- a/azext_iot/digitaltwins/params.py +++ b/azext_iot/digitaltwins/params.py @@ -12,7 +12,13 @@ from azure.cli.core.commands.parameters import ( resource_group_name_type, get_three_state_flag, - tags_type + get_enum_type, + tags_type, +) +from azext_iot.digitaltwins.common import ( + ADTEndpointAuthType, + ADTPrivateConnectionStatusType, + ADTPublicNetworkAccessType, ) depfor_type = CLIArgumentType( @@ -70,10 +76,14 @@ def load_digitaltwins_arguments(self, _): help="Event route name.", ) context.argument( - "filter", options_list=["--filter"], help="Event route filter.", + "filter", + options_list=["--filter"], + help="Event route filter.", ) context.argument( - "role_type", options_list=["--role"], help="Role name or Id.", + "role_type", + options_list=["--role"], + help="Role name or Id.", ) context.argument( "assignee", @@ -87,7 +97,9 @@ def load_digitaltwins_arguments(self, _): help="Digital Twins model Id. Example: dtmi:com:example:Room;2", ) context.argument( - "twin_id", options_list=["--twin-id", "-t"], help="The digital twin Id.", + "twin_id", + options_list=["--twin-id", "-t"], + help="The digital twin Id.", ) context.argument( "include_inherited", @@ -101,13 +113,53 @@ def load_digitaltwins_arguments(self, _): options_list=["--top"], help="Maximum number of elements to return.", ) + context.argument( + "public_network_access", + options_list=["--public-network-access", "--pna"], + help="Determines if the Digital Twins instance can be accessed from a public network.", + arg_group="Networking", + arg_type=get_enum_type(ADTPublicNetworkAccessType), + ) + + with self.argument_context("dt create") as context: + context.argument( + "assign_identity", + arg_group="Managed Service Identity", + help="Assign a system generated identity to the Digital Twins instance.", + arg_type=get_three_state_flag(), + ) + context.argument( + "scopes", + arg_group="Managed Service Identity", + nargs="+", + options_list=["--scopes"], + help="Space-seperated scopes the system assigned identity can access.", + ) + context.argument( + "role_type", + arg_group="Managed Service Identity", + options_list=["--role"], + help="Role name or Id the system assigned identity will have.", + ) with self.argument_context("dt endpoint create") as context: context.argument( - "dead_letter_endpoint", + "dead_letter_secret", options_list=["--deadletter-sas-uri", "--dsu"], - help="Dead-letter storage container URL with SAS token", - arg_group="Dead-letter Endpoint" + help="Dead-letter storage container URL with SAS token for Key based authentication.", + arg_group="Dead-letter Endpoint", + ) + context.argument( + "dead_letter_uri", + options_list=["--deadletter-uri", "--du"], + help="Dead-letter storage container URL for Identity based authentication.", + arg_group="Dead-letter Endpoint", + ) + context.argument( + "auth_type", + options_list=["--auth-type"], + help="Endpoint authentication type.", + arg_type=get_enum_type(ADTEndpointAuthType), ) with self.argument_context("dt endpoint create eventgrid") as context: @@ -141,7 +193,7 @@ def load_digitaltwins_arguments(self, _): context.argument( "eventhub_policy", options_list=["--eventhub-policy", "--ehp"], - help="EventHub policy to use for endpoint configuration.", + help="EventHub policy to use for endpoint configuration. Required when --auth-type is KeyBased.", arg_group="Event Hub", ) context.argument( @@ -174,7 +226,7 @@ def load_digitaltwins_arguments(self, _): context.argument( "servicebus_policy", options_list=["--servicebus-policy", "--sbp"], - help="ServiceBus Topic policy to use for endpoint configuration.", + help="ServiceBus Topic policy to use for endpoint configuration. Required when --auth-type is KeyBased.", arg_group="Service Bus Topic", ) context.argument( @@ -317,5 +369,48 @@ def load_digitaltwins_arguments(self, _): help="Indicates intent to decommission a target model.", ) context.argument( - "dependencies_for", arg_type=depfor_type, + "dependencies_for", + arg_type=depfor_type, + ) + + with self.argument_context("dt network private-link") as context: + context.argument( + "link_name", + options_list=["--link-name", "--ln"], + help="Private link name.", + arg_group="Private Connection", + ) + + with self.argument_context("dt network private-endpoint") as context: + context.argument( + "conn_name", + options_list=["--conn-name", "--cn"], + help="Private endpoint connection name.", + arg_group="Private-Endpoint", + ) + context.argument( + "group_ids", + options_list=["--group-ids"], + help="Space seperated list of group ids that the private endpoint should connect to.", + arg_group="Private-Endpoint", + nargs="+", + ) + context.argument( + "status", + options_list=["--status"], + help="The status of a private endpoint connection.", + arg_type=get_enum_type(ADTPrivateConnectionStatusType), + arg_group="Private-Endpoint", + ) + context.argument( + "description", + options_list=["--description", "--desc"], + help="Description for the private endpoint connection.", + arg_group="Private-Endpoint", + ) + context.argument( + "actions_required", + options_list=["--actions-required", "--ar"], + help="A message indicating if changes on the service provider require any updates on the consumer.", + arg_group="Private-Endpoint", ) diff --git a/azext_iot/digitaltwins/providers/__init__.py b/azext_iot/digitaltwins/providers/__init__.py index 274d3cfc4..c8e0929b6 100644 --- a/azext_iot/digitaltwins/providers/__init__.py +++ b/azext_iot/digitaltwins/providers/__init__.py @@ -7,6 +7,7 @@ from azext_iot.sdk.digitaltwins.controlplane import AzureDigitalTwinsManagementClient from azext_iot.sdk.digitaltwins.controlplane.models import ErrorResponseException from msrestazure.azure_exceptions import CloudError +from azext_iot.constants import USER_AGENT __all__ = [ "digitaltwins_service_factory", @@ -16,7 +17,7 @@ ] -def digitaltwins_service_factory(cli_ctx, *_): +def digitaltwins_service_factory(cli_ctx, *_) -> AzureDigitalTwinsManagementClient: """ Factory for importing deps and getting service client resources. @@ -25,8 +26,7 @@ def digitaltwins_service_factory(cli_ctx, *_): *_ : all other args ignored. Returns: - iot_hub_resource (IotHubClient.iot_hub_resource): operational resource for - working with IoT Hub. + AzureDigitalTwinsManagementClient: Top level client instance. """ from azure.cli.core.commands.client_factory import get_mgmt_service_client @@ -39,4 +39,6 @@ def __init__(self, cmd): self.cmd = cmd def get_mgmt_sdk(self): - return digitaltwins_service_factory(self.cmd.cli_ctx) + client = digitaltwins_service_factory(self.cmd.cli_ctx) + client.config.add_user_agent(USER_AGENT) + return client diff --git a/azext_iot/digitaltwins/providers/base.py b/azext_iot/digitaltwins/providers/base.py index c93d94aad..4670b92cb 100644 --- a/azext_iot/digitaltwins/providers/base.py +++ b/azext_iot/digitaltwins/providers/base.py @@ -5,10 +5,9 @@ # -------------------------------------------------------------------------------------------- from azext_iot.digitaltwins.providers.resource import ResourceProvider -from azext_iot.digitaltwins.providers.auth import DigitalTwinAuthentication from azext_iot.sdk.digitaltwins.dataplane import AzureDigitalTwinsAPI from azext_iot.sdk.digitaltwins.dataplane.models import ErrorResponseException -from azext_iot.constants import DIGITALTWINS_RESOURCE_ID +from azext_iot.constants import DIGITALTWINS_RESOURCE_ID, USER_AGENT from azext_iot.common.utility import valid_hostname from knack.cli import CLIError @@ -32,12 +31,14 @@ def _get_endpoint(self): http_prefix = "http://" if self.name.lower().startswith(https_prefix): - self.name = self.name[len(https_prefix):] + self.name = self.name[len(https_prefix) :] elif self.name.lower().startswith(http_prefix): - self.name = self.name[len(http_prefix):] + self.name = self.name[len(http_prefix) :] if not all([valid_hostname(self.name), "." in self.name]): - instance = self.rp.find_instance(name=self.name, resource_group_name=self.rg) + instance = self.rp.find_instance( + name=self.name, resource_group_name=self.rg + ) host_name = instance.host_name if not host_name: raise CLIError("Instance has invalid hostName. Aborting operation...") @@ -47,5 +48,16 @@ def _get_endpoint(self): return "https://{}".format(host_name) def get_sdk(self): - creds = DigitalTwinAuthentication(cmd=self.cmd, resource_id=self.resource_id) - return AzureDigitalTwinsAPI(base_url=self._get_endpoint(), credentials=creds) + from azure.cli.core.commands.client_factory import get_mgmt_service_client + + client = get_mgmt_service_client( + cli_ctx=self.cmd.cli_ctx, + client_or_resource_type=AzureDigitalTwinsAPI, + base_url=self._get_endpoint(), + resource=self.resource_id, + subscription_bound=False, + base_url_bound=False, + ) + + client.config.add_user_agent(USER_AGENT) + return client diff --git a/azext_iot/digitaltwins/providers/endpoint/__init__.py b/azext_iot/digitaltwins/providers/endpoint/__init__.py new file mode 100644 index 000000000..55614acbf --- /dev/null +++ b/azext_iot/digitaltwins/providers/endpoint/__init__.py @@ -0,0 +1,5 @@ +# 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. +# -------------------------------------------------------------------------------------------- diff --git a/azext_iot/digitaltwins/providers/endpoint/builders.py b/azext_iot/digitaltwins/providers/endpoint/builders.py new file mode 100644 index 000000000..73e05c581 --- /dev/null +++ b/azext_iot/digitaltwins/providers/endpoint/builders.py @@ -0,0 +1,330 @@ +# 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 azext_iot.common.embedded_cli import EmbeddedCLI +from azext_iot.digitaltwins.common import ADTEndpointAuthType +from abc import ABC, abstractmethod +from knack.util import CLIError +from knack.log import get_logger + +from azext_iot.sdk.digitaltwins.controlplane.models import ( + EventGrid as EventGridEndpointProperties, + EventHub as EventHubEndpointProperties, + ServiceBus as ServiceBusEndpointProperties, +) + +logger = get_logger(__name__) + + +class BaseEndpointBuilder(ABC): + def __init__( + self, + endpoint_resource_name: str, + endpoint_resource_group: str, + auth_type: str = ADTEndpointAuthType.keybased.value, + dead_letter_secret: str = None, + dead_letter_uri: str = None, + endpoint_subscription: str = None, + ): + self.cli = EmbeddedCLI() + self.error_prefix = "Could not create ADT instance endpoint. Unable to retrieve" + self.endpoint_resource_name = endpoint_resource_name + self.endpoint_resource_group = endpoint_resource_group + self.endpoint_subscription = endpoint_subscription + self.auth_type = auth_type + self.dead_letter_secret = dead_letter_secret + self.dead_letter_uri = dead_letter_uri + + def build_endpoint(self): + endpoint_properties = ( + self.build_key_based() + if self.auth_type == ADTEndpointAuthType.keybased.value + else self.build_identity_based() + ) + endpoint_properties.authentication_type = self.auth_type + return endpoint_properties + + @abstractmethod + def build_key_based(self): + pass + + @abstractmethod + def build_identity_based(self): + pass + + +class EventGridEndpointBuilder(BaseEndpointBuilder): + def __init__( + self, + endpoint_resource_name, + endpoint_resource_group, + auth_type=ADTEndpointAuthType.keybased.value, + dead_letter_secret=None, + dead_letter_uri=None, + endpoint_subscription=None, + ): + super().__init__( + endpoint_resource_name=endpoint_resource_name, + endpoint_resource_group=endpoint_resource_group, + auth_type=auth_type, + dead_letter_secret=dead_letter_secret, + dead_letter_uri=dead_letter_uri, + endpoint_subscription=endpoint_subscription, + ) + + def build_key_based(self): + eg_topic_keys_op = self.cli.invoke( + "eventgrid topic key list -n {} -g {}".format( + self.endpoint_resource_name, self.endpoint_resource_group + ), + subscription=self.endpoint_subscription, + ) + if not eg_topic_keys_op.success(): + raise CLIError("{} Event Grid topic keys.".format(self.error_prefix)) + eg_topic_keys = eg_topic_keys_op.as_json() + + eg_topic_endpoint_op = self.cli.invoke( + "eventgrid topic show -n {} -g {}".format( + self.endpoint_resource_name, self.endpoint_resource_group + ), + subscription=self.endpoint_subscription, + ) + if not eg_topic_endpoint_op.success(): + raise CLIError("{} Event Grid topic endpoint.".format(self.error_prefix)) + eg_topic_endpoint = eg_topic_endpoint_op.as_json() + + # TODO: Potentionally have shared attributes handled by build_endpoint() + return EventGridEndpointProperties( + access_key1=eg_topic_keys["key1"], + access_key2=eg_topic_keys["key2"], + dead_letter_secret=self.dead_letter_secret, + dead_letter_uri=self.dead_letter_uri, + topic_endpoint=eg_topic_endpoint["endpoint"], + ) + + def build_identity_based(self): + raise CLIError( + "Identity based EventGrid endpoint creation is not yet supported. " + ) + + +class ServiceBusEndpointBuilder(BaseEndpointBuilder): + def __init__( + self, + endpoint_resource_name, + endpoint_resource_group, + endpoint_resource_namespace, + endpoint_resource_policy, + auth_type=ADTEndpointAuthType.keybased.value, + dead_letter_secret=None, + dead_letter_uri=None, + endpoint_subscription=None, + ): + super().__init__( + endpoint_resource_name=endpoint_resource_name, + endpoint_resource_group=endpoint_resource_group, + auth_type=auth_type, + dead_letter_secret=dead_letter_secret, + dead_letter_uri=dead_letter_uri, + endpoint_subscription=endpoint_subscription, + ) + self.endpoint_resource_namespace = endpoint_resource_namespace + self.endpoint_resource_policy = endpoint_resource_policy + + def build_key_based(self): + sb_topic_keys_op = self.cli.invoke( + "servicebus topic authorization-rule keys list -n {} " + "--namespace-name {} -g {} --topic-name {}".format( + self.endpoint_resource_policy, + self.endpoint_resource_namespace, + self.endpoint_resource_group, + self.endpoint_resource_name, + ), + subscription=self.endpoint_subscription, + ) + if not sb_topic_keys_op.success(): + raise CLIError("{} Service Bus topic keys.".format(self.error_prefix)) + sb_topic_keys = sb_topic_keys_op.as_json() + + return ServiceBusEndpointProperties( + primary_connection_string=sb_topic_keys["primaryConnectionString"], + secondary_connection_string=sb_topic_keys["secondaryConnectionString"], + dead_letter_secret=self.dead_letter_secret, + dead_letter_uri=self.dead_letter_uri, + ) + + def build_identity_based(self): + sb_namespace_op = self.cli.invoke( + "servicebus namespace show --name {} -g {}".format( + self.endpoint_resource_namespace, + self.endpoint_resource_group, + ), + subscription=self.endpoint_subscription, + ) + if not sb_namespace_op.success(): + raise CLIError("{} Service Bus Namespace.".format(self.error_prefix)) + sb_namespace_meta = sb_namespace_op.as_json() + sb_endpoint = sb_namespace_meta["serviceBusEndpoint"] + + sb_topic_op = self.cli.invoke( + "servicebus topic show --name {} --namespace {} -g {}".format( + self.endpoint_resource_name, + self.endpoint_resource_namespace, + self.endpoint_resource_group, + ), + subscription=self.endpoint_subscription, + ) + + if not sb_topic_op.success(): + raise CLIError("{} Service Bus Topic.".format(self.error_prefix)) + + return ServiceBusEndpointProperties( + endpoint_uri=transform_sb_hostname_to_schemauri(sb_endpoint), + entity_path=self.endpoint_resource_name, + dead_letter_secret=self.dead_letter_secret, + dead_letter_uri=self.dead_letter_uri, + ) + + +class EventHubEndpointBuilder(BaseEndpointBuilder): + def __init__( + self, + endpoint_resource_name, + endpoint_resource_group, + endpoint_resource_namespace, + endpoint_resource_policy, + auth_type=ADTEndpointAuthType.keybased.value, + dead_letter_secret=None, + dead_letter_uri=None, + endpoint_subscription=None, + ): + super().__init__( + endpoint_resource_name=endpoint_resource_name, + endpoint_resource_group=endpoint_resource_group, + auth_type=auth_type, + dead_letter_secret=dead_letter_secret, + dead_letter_uri=dead_letter_uri, + endpoint_subscription=endpoint_subscription, + ) + self.endpoint_resource_namespace = endpoint_resource_namespace + self.endpoint_resource_policy = endpoint_resource_policy + + def build_key_based(self): + eventhub_topic_keys_op = self.cli.invoke( + "eventhubs eventhub authorization-rule keys list -n {} " + "--namespace-name {} -g {} --eventhub-name {}".format( + self.endpoint_resource_policy, + self.endpoint_resource_namespace, + self.endpoint_resource_group, + self.endpoint_resource_name, + ), + subscription=self.endpoint_subscription, + ) + if not eventhub_topic_keys_op.success(): + raise CLIError("{} Event Hub keys.".format(self.error_prefix)) + eventhub_topic_keys = eventhub_topic_keys_op.as_json() + + return EventHubEndpointProperties( + connection_string_primary_key=eventhub_topic_keys[ + "primaryConnectionString" + ], + connection_string_secondary_key=eventhub_topic_keys[ + "secondaryConnectionString" + ], + dead_letter_secret=self.dead_letter_secret, + dead_letter_uri=self.dead_letter_uri, + ) + + def build_identity_based(self): + sb_namespace_op = self.cli.invoke( + "eventhubs namespace show --name {} -g {}".format( + self.endpoint_resource_namespace, + self.endpoint_resource_group, + ), + subscription=self.endpoint_subscription, + ) + if not sb_namespace_op.success(): + raise CLIError("{} EventHub Namespace.".format(self.error_prefix)) + sb_namespace_meta = sb_namespace_op.as_json() + sb_endpoint = sb_namespace_meta["serviceBusEndpoint"] + + sb_topic_op = self.cli.invoke( + "eventhubs eventhub show --name {} --namespace {} -g {}".format( + self.endpoint_resource_name, + self.endpoint_resource_namespace, + self.endpoint_resource_group, + ), + subscription=self.endpoint_subscription, + ) + + if not sb_topic_op.success(): + raise CLIError("{} EventHub.".format(self.error_prefix)) + + return EventHubEndpointProperties( + endpoint_uri=transform_sb_hostname_to_schemauri(sb_endpoint), + entity_path=self.endpoint_resource_name, + dead_letter_secret=self.dead_letter_secret, + dead_letter_uri=self.dead_letter_uri, + ) + + +def transform_sb_hostname_to_schemauri(endpoint): + from urllib.parse import urlparse + + sb_endpoint_parts = urlparse(endpoint) + sb_hostname = sb_endpoint_parts.hostname + sb_schema_uri = "sb://{}/".format(sb_hostname) + return sb_schema_uri + + +def build_endpoint( + endpoint_resource_type: str, + endpoint_resource_name: str, + endpoint_resource_group: str, + auth_type: str = ADTEndpointAuthType.keybased.value, + endpoint_resource_namespace: str = None, + endpoint_resource_policy: str = None, + dead_letter_secret: str = None, + dead_letter_uri: str = None, + endpoint_subscription: str = None, +): + from azext_iot.digitaltwins.common import ADTEndpointType + + if endpoint_resource_type == ADTEndpointType.eventgridtopic.value: + return EventGridEndpointBuilder( + endpoint_resource_name=endpoint_resource_name, + endpoint_resource_group=endpoint_resource_group, + auth_type=auth_type, + dead_letter_secret=dead_letter_secret, + dead_letter_uri=dead_letter_uri, + endpoint_subscription=endpoint_subscription, + ).build_endpoint() + + if endpoint_resource_type == ADTEndpointType.servicebus.value: + return ServiceBusEndpointBuilder( + endpoint_resource_name=endpoint_resource_name, + endpoint_resource_group=endpoint_resource_group, + auth_type=auth_type, + dead_letter_secret=dead_letter_secret, + dead_letter_uri=dead_letter_uri, + endpoint_subscription=endpoint_subscription, + endpoint_resource_namespace=endpoint_resource_namespace, + endpoint_resource_policy=endpoint_resource_policy, + ).build_endpoint() + + if endpoint_resource_type == ADTEndpointType.eventhub.value: + return EventHubEndpointBuilder( + endpoint_resource_name=endpoint_resource_name, + endpoint_resource_group=endpoint_resource_group, + auth_type=auth_type, + dead_letter_secret=dead_letter_secret, + dead_letter_uri=dead_letter_uri, + endpoint_subscription=endpoint_subscription, + endpoint_resource_namespace=endpoint_resource_namespace, + endpoint_resource_policy=endpoint_resource_policy, + ).build_endpoint() + + raise ValueError("{} not supported.".format(endpoint_resource_type)) diff --git a/azext_iot/digitaltwins/providers/rbac.py b/azext_iot/digitaltwins/providers/rbac.py index 0e2710b65..a449a54f5 100644 --- a/azext_iot/digitaltwins/providers/rbac.py +++ b/azext_iot/digitaltwins/providers/rbac.py @@ -57,3 +57,14 @@ def remove_role(self, dt_scope, assignee, role_type=None): if not delete_op.success(): raise CLIError("Unable to remove role assignment.") return + + def assign_role_flex(self, principal_id, scope, principal_type="ServicePrincipal", role_type="Contributor"): + assign_op = self.cli.invoke( + "role assignment create --scope '{}' --role '{}' --assignee-object-id '{}' --assignee-principal-type '{}' ".format( + scope, role_type, principal_id, principal_type + ) + ) + if not assign_op.success(): + raise CLIError("Unable to assign role.") + + return assign_op.as_json() diff --git a/azext_iot/digitaltwins/providers/resource.py b/azext_iot/digitaltwins/providers/resource.py index 97db61f7b..f643764e7 100644 --- a/azext_iot/digitaltwins/providers/resource.py +++ b/azext_iot/digitaltwins/providers/resource.py @@ -3,7 +3,10 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- - +from azext_iot.digitaltwins.common import ( + ADTEndpointAuthType, + ADTPublicNetworkAccessType, +) from azext_iot.digitaltwins.providers import ( DigitalTwinsResourceManager, CloudError, @@ -11,12 +14,13 @@ ) from azext_iot.digitaltwins.providers.rbac import RbacProvider from azext_iot.sdk.digitaltwins.controlplane.models import ( - EventGrid as EventGridEndpointProperties, - EventHub as EventHubEndpointProperties, - ServiceBus as ServiceBusEndpointProperties, + DigitalTwinsDescription, ) from azext_iot.common.utility import unpack_msrest_error from knack.util import CLIError +from knack.log import get_logger + +logger = get_logger(__name__) class ResourceProvider(DigitalTwinsResourceManager): @@ -25,8 +29,18 @@ def __init__(self, cmd): self.mgmt_sdk = self.get_mgmt_sdk() self.rbac = RbacProvider() - def create(self, name, resource_group_name, location=None, tags=None, timeout=60): - + def create( + self, + name, + resource_group_name, + location=None, + tags=None, + timeout=60, + assign_identity=None, + scopes=None, + role_type="Contributor", + public_network_access=ADTPublicNetworkAccessType.enabled.value, + ): if not location: from azext_iot.common.embedded_cli import EmbeddedCLI @@ -38,13 +52,52 @@ def create(self, name, resource_group_name, location=None, tags=None, timeout=60 location = resource_group_meta["location"] try: - return self.mgmt_sdk.digital_twins.create_or_update( - resource_name=name, - resource_group_name=resource_group_name, + if assign_identity: + if scopes and not role_type: + raise CLIError( + "Both --scopes and --role values are required when assigning the instance identity." + ) + + digital_twins_create = DigitalTwinsDescription( location=location, tags=tags, + identity={"type": "SystemAssigned" if assign_identity else "None"}, + public_network_access=public_network_access, + ) + create_or_update = self.mgmt_sdk.digital_twins.create_or_update( + resource_name=name, + resource_group_name=resource_group_name, + digital_twins_create=digital_twins_create, long_running_operation_timeout=timeout, ) + + def rbac_handler(lro): + instance = lro.resource().as_dict() + identity = instance.get("identity") + if identity: + identity_type = identity.get("type") + principal_id = identity.get("principal_id") + + if ( + principal_id + and scopes + and identity_type + and identity_type.lower() == "systemassigned" + ): + for scope in scopes: + logger.info( + "Applying rbac assignment: Principal Id: {}, Scope: {}, Role: {}".format( + principal_id, scope, role_type + ) + ) + self.rbac.assign_role_flex( + principal_id=principal_id, + scope=scope, + role_type=role_type, + ) + + create_or_update.add_done_callback(rbac_handler) + return create_or_update except CloudError as e: raise e except ErrorResponseException as err: @@ -216,7 +269,6 @@ def delete_endpoint(self, name, endpoint_name, resource_group_name=None): except ErrorResponseException as e: raise CLIError(unpack_msrest_error(e)) - # TODO: Breakout and refactor def add_endpoint( self, name, @@ -227,114 +279,65 @@ def add_endpoint( endpoint_resource_policy=None, endpoint_resource_namespace=None, endpoint_subscription=None, - dead_letter_endpoint=None, - tags=None, + dead_letter_uri=None, + dead_letter_secret=None, resource_group_name=None, timeout=20, + auth_type=None, ): - from azext_iot.common.embedded_cli import EmbeddedCLI from azext_iot.digitaltwins.common import ADTEndpointType - requires_policy = [ADTEndpointType.eventhub, ADTEndpointType.servicebus] - if endpoint_resource_type in requires_policy: - if not endpoint_resource_policy: + requires_namespace = [ + ADTEndpointType.eventhub.value, + ADTEndpointType.servicebus.value, + ] + if endpoint_resource_type in requires_namespace: + if not endpoint_resource_namespace: raise CLIError( - "Endpoint resources of type {} require a policy name.".format( - " or ".join(map(str, requires_policy)) + "Endpoint resources of type {} require a namespace.".format( + " or ".join(map(str, requires_namespace)) ) ) - if not endpoint_resource_namespace: + if ( + auth_type == ADTEndpointAuthType.keybased.value + and not endpoint_resource_policy + ): raise CLIError( - "Endpoint resources of type {} require a namespace.".format( - " or ".join(map(str, requires_policy)) + "Endpoint resources of type {} require a policy name when using Key based integration.".format( + " or ".join(map(str, requires_namespace)) ) ) + if dead_letter_uri and auth_type == ADTEndpointAuthType.keybased.value: + raise CLIError( + "Use --deadletter-sas-uri to support deadletter for a Key based endpoint." + ) + + if dead_letter_secret and auth_type == ADTEndpointAuthType.identitybased.value: + raise CLIError( + "Use --deadletter-uri to support deadletter for an Identity based endpoint." + ) + target_instance = self.find_instance( name=name, resource_group_name=resource_group_name ) if not resource_group_name: resource_group_name = self.get_rg(target_instance) - cli = EmbeddedCLI() - error_prefix = "Could not create ADT instance endpoint. Unable to retrieve" - - properties = {} - - if endpoint_resource_type == ADTEndpointType.eventgridtopic: - eg_topic_keys_op = cli.invoke( - "eventgrid topic key list -n {} -g {}".format( - endpoint_resource_name, endpoint_resource_group - ), - subscription=endpoint_subscription, - ) - if not eg_topic_keys_op.success(): - raise CLIError("{} Event Grid topic keys.".format(error_prefix)) - eg_topic_keys = eg_topic_keys_op.as_json() - - eg_topic_endpoint_op = cli.invoke( - "eventgrid topic show -n {} -g {}".format( - endpoint_resource_name, endpoint_resource_group - ), - subscription=endpoint_subscription, - ) - if not eg_topic_endpoint_op.success(): - raise CLIError("{} Event Grid topic endpoint.".format(error_prefix)) - eg_topic_endpoint = eg_topic_endpoint_op.as_json() - - properties = EventGridEndpointProperties( - access_key1=eg_topic_keys["key1"], - access_key2=eg_topic_keys["key2"], - dead_letter_secret=dead_letter_endpoint, - topic_endpoint=eg_topic_endpoint["endpoint"], - ) - - elif endpoint_resource_type == ADTEndpointType.servicebus: - sb_topic_keys_op = cli.invoke( - "servicebus topic authorization-rule keys list -n {} " - "--namespace-name {} -g {} --topic-name {}".format( - endpoint_resource_policy, - endpoint_resource_namespace, - endpoint_resource_group, - endpoint_resource_name, - ), - subscription=endpoint_subscription, - ) - if not sb_topic_keys_op.success(): - raise CLIError("{} Service Bus topic keys.".format(error_prefix)) - sb_topic_keys = sb_topic_keys_op.as_json() - - properties = ServiceBusEndpointProperties( - primary_connection_string=sb_topic_keys["primaryConnectionString"], - secondary_connection_string=sb_topic_keys["secondaryConnectionString"], - dead_letter_secret=dead_letter_endpoint, - ) - - elif endpoint_resource_type == ADTEndpointType.eventhub: - eventhub_topic_keys_op = cli.invoke( - "eventhubs eventhub authorization-rule keys list -n {} " - "--namespace-name {} -g {} --eventhub-name {}".format( - endpoint_resource_policy, - endpoint_resource_namespace, - endpoint_resource_group, - endpoint_resource_name, - ), - subscription=endpoint_subscription, - ) - if not eventhub_topic_keys_op.success(): - raise CLIError("{} Event Hub keys.".format(error_prefix)) - eventhub_topic_keys = eventhub_topic_keys_op.as_json() - - properties = EventHubEndpointProperties( - connection_string_primary_key=eventhub_topic_keys[ - "primaryConnectionString" - ], - connection_string_secondary_key=eventhub_topic_keys[ - "secondaryConnectionString" - ], - dead_letter_secret=dead_letter_endpoint, - ) + from azext_iot.digitaltwins.providers.endpoint.builders import build_endpoint + + properties = build_endpoint( + endpoint_resource_type=endpoint_resource_type, + endpoint_resource_name=endpoint_resource_name, + endpoint_resource_group=endpoint_resource_group, + endpoint_subscription=endpoint_subscription, + endpoint_resource_namespace=endpoint_resource_namespace, + endpoint_resource_policy=endpoint_resource_policy, + auth_type=auth_type, + dead_letter_secret=dead_letter_secret, + dead_letter_uri=dead_letter_uri, + ) try: return self.mgmt_sdk.digital_twins_endpoint.create_or_update( @@ -346,3 +349,119 @@ def add_endpoint( ) except ErrorResponseException as e: raise CLIError(unpack_msrest_error(e)) + + def get_private_link(self, name, link_name, resource_group_name=None): + target_instance = self.find_instance( + name=name, resource_group_name=resource_group_name + ) + if not resource_group_name: + resource_group_name = self.get_rg(target_instance) + + try: + return self.mgmt_sdk.private_link_resources.get( + resource_group_name=resource_group_name, + resource_name=name, + resource_id=link_name, + raw=True, + ).response.json() + except ErrorResponseException as e: + raise CLIError(unpack_msrest_error(e)) + + def list_private_links(self, name, resource_group_name=None): + target_instance = self.find_instance( + name=name, resource_group_name=resource_group_name + ) + if not resource_group_name: + resource_group_name = self.get_rg(target_instance) + + try: + # This resource is not paged though it may have been the intent. + link_collection = self.mgmt_sdk.private_link_resources.list( + resource_group_name=resource_group_name, resource_name=name, raw=True + ).response.json() + return link_collection.get("value", []) + except ErrorResponseException as e: + raise CLIError(unpack_msrest_error(e)) + + def set_private_endpoint_conn( + self, + name, + conn_name, + status, + description, + actions_required=None, + group_ids=None, + resource_group_name=None, + ): + target_instance = self.find_instance( + name=name, resource_group_name=resource_group_name + ) + if not resource_group_name: + resource_group_name = self.get_rg(target_instance) + + try: + return self.mgmt_sdk.private_endpoint_connections.create_or_update( + resource_group_name=resource_group_name, + resource_name=name, + private_endpoint_connection_name=conn_name, + properties={ + "privateLinkServiceConnectionState": { + "status": status, + "description": description, + "actions_required": actions_required, + }, + "groupIds": group_ids, + }, + ) + + except ErrorResponseException as e: + raise CLIError(unpack_msrest_error(e)) + + def get_private_endpoint_conn(self, name, conn_name, resource_group_name=None): + target_instance = self.find_instance( + name=name, resource_group_name=resource_group_name + ) + if not resource_group_name: + resource_group_name = self.get_rg(target_instance) + + try: + return self.mgmt_sdk.private_endpoint_connections.get( + resource_group_name=resource_group_name, + resource_name=name, + private_endpoint_connection_name=conn_name, + raw=True, + ).response.json() + except ErrorResponseException as e: + raise CLIError(unpack_msrest_error(e)) + + def list_private_endpoint_conns(self, name, resource_group_name=None): + target_instance = self.find_instance( + name=name, resource_group_name=resource_group_name + ) + if not resource_group_name: + resource_group_name = self.get_rg(target_instance) + + try: + # This resource is not paged though it may have been the intent. + endpoint_collection = self.mgmt_sdk.private_endpoint_connections.list( + resource_group_name=resource_group_name, resource_name=name, raw=True + ).response.json() + return endpoint_collection.get("value", []) + except ErrorResponseException as e: + raise CLIError(unpack_msrest_error(e)) + + def delete_private_endpoint_conn(self, name, conn_name, resource_group_name=None): + target_instance = self.find_instance( + name=name, resource_group_name=resource_group_name + ) + if not resource_group_name: + resource_group_name = self.get_rg(target_instance) + + try: + return self.mgmt_sdk.private_endpoint_connections.delete( + resource_group_name=resource_group_name, + resource_name=name, + private_endpoint_connection_name=conn_name + ) + except ErrorResponseException as e: + raise CLIError(unpack_msrest_error(e)) diff --git a/azext_iot/digitaltwins/providers/twin.py b/azext_iot/digitaltwins/providers/twin.py index 8e36aa2f3..b9f64c96f 100644 --- a/azext_iot/digitaltwins/providers/twin.py +++ b/azext_iot/digitaltwins/providers/twin.py @@ -20,7 +20,9 @@ class TwinProvider(DigitalTwinsProvider): def __init__(self, cmd, name, rg=None): super(TwinProvider, self).__init__( - cmd=cmd, name=name, rg=rg, + cmd=cmd, + name=name, + rg=rg, ) self.model_provider = ModelProvider(cmd=cmd, name=name, rg=rg) self.query_sdk = self.get_sdk().query diff --git a/azext_iot/sdk/digitaltwins/controlplane/azure_digital_twins_management_client.py b/azext_iot/sdk/digitaltwins/controlplane/azure_digital_twins_management_client.py index 1f6f0e312..03e9b6150 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/azure_digital_twins_management_client.py +++ b/azext_iot/sdk/digitaltwins/controlplane/azure_digital_twins_management_client.py @@ -16,8 +16,9 @@ from .operations.digital_twins_operations import DigitalTwinsOperations from .operations.digital_twins_endpoint_operations import DigitalTwinsEndpointOperations from .operations.operations import Operations +from .operations.private_link_resources_operations import PrivateLinkResourcesOperations +from .operations.private_endpoint_connections_operations import PrivateEndpointConnectionsOperations from . import models -from azext_iot.constants import USER_AGENT class AzureDigitalTwinsManagementClientConfiguration(AzureConfiguration): @@ -46,7 +47,6 @@ def __init__( super(AzureDigitalTwinsManagementClientConfiguration, self).__init__(base_url) self.add_user_agent('azure-mgmt-digitaltwins/{}'.format(VERSION)) - self.add_user_agent(USER_AGENT) self.credentials = credentials self.subscription_id = subscription_id @@ -59,11 +59,15 @@ class AzureDigitalTwinsManagementClient(SDKClient): :vartype config: AzureDigitalTwinsManagementClientConfiguration :ivar digital_twins: DigitalTwins operations - :vartype digital_twins: azure.mgmt.digitaltwins.operations.DigitalTwinsOperations + :vartype digital_twins: controlplane.operations.DigitalTwinsOperations :ivar digital_twins_endpoint: DigitalTwinsEndpoint operations - :vartype digital_twins_endpoint: azure.mgmt.digitaltwins.operations.DigitalTwinsEndpointOperations + :vartype digital_twins_endpoint: controlplane.operations.DigitalTwinsEndpointOperations :ivar operations: Operations operations - :vartype operations: azure.mgmt.digitaltwins.operations.Operations + :vartype operations: controlplane.operations.Operations + :ivar private_link_resources: PrivateLinkResources operations + :vartype private_link_resources: controlplane.operations.PrivateLinkResourcesOperations + :ivar private_endpoint_connections: PrivateEndpointConnections operations + :vartype private_endpoint_connections: controlplane.operations.PrivateEndpointConnectionsOperations :param credentials: Credentials needed for the client to connect to Azure. :type credentials: :mod:`A msrestazure Credentials @@ -80,7 +84,7 @@ def __init__( super(AzureDigitalTwinsManagementClient, self).__init__(self.config.credentials, self.config) client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} - self.api_version = '2020-10-31' + self.api_version = '2020-12-01' self._serialize = Serializer(client_models) self._deserialize = Deserializer(client_models) @@ -90,3 +94,7 @@ def __init__( self._client, self.config, self._serialize, self._deserialize) self.operations = Operations( self._client, self.config, self._serialize, self._deserialize) + self.private_link_resources = PrivateLinkResourcesOperations( + self._client, self.config, self._serialize, self._deserialize) + self.private_endpoint_connections = PrivateEndpointConnectionsOperations( + self._client, self.config, self._serialize, self._deserialize) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/__init__.py b/azext_iot/sdk/digitaltwins/controlplane/models/__init__.py index 92d8cf33c..ed6089378 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/__init__.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/__init__.py @@ -10,7 +10,11 @@ # -------------------------------------------------------------------------- try: + from .digital_twins_patch_properties_py3 import DigitalTwinsPatchProperties + from .private_endpoint_connection_properties_py3 import PrivateEndpointConnectionProperties + from .private_endpoint_connection_py3 import PrivateEndpointConnection from .digital_twins_description_py3 import DigitalTwinsDescription + from .digital_twins_identity_py3 import DigitalTwinsIdentity from .digital_twins_patch_description_py3 import DigitalTwinsPatchDescription from .digital_twins_resource_py3 import DigitalTwinsResource from .error_definition_py3 import ErrorDefinition @@ -25,8 +29,22 @@ from .service_bus_py3 import ServiceBus from .event_hub_py3 import EventHub from .event_grid_py3 import EventGrid + from .group_id_information_properties_py3 import GroupIdInformationProperties + from .group_id_information_properties_model_py3 import GroupIdInformationPropertiesModel + from .group_id_information_py3 import GroupIdInformation + from .private_endpoint_connections_response_py3 import PrivateEndpointConnectionsResponse + from .group_id_information_response_py3 import GroupIdInformationResponse + from .connection_state_py3 import ConnectionState + from .private_endpoint_py3 import PrivateEndpoint + from .connection_properties_private_endpoint_py3 import ConnectionPropertiesPrivateEndpoint + from .connection_properties_private_link_service_connection_state_py3 import ConnectionPropertiesPrivateLinkServiceConnectionState + from .connection_properties_py3 import ConnectionProperties except (SyntaxError, ImportError): + from .digital_twins_patch_properties import DigitalTwinsPatchProperties + from .private_endpoint_connection_properties import PrivateEndpointConnectionProperties + from .private_endpoint_connection import PrivateEndpointConnection from .digital_twins_description import DigitalTwinsDescription + from .digital_twins_identity import DigitalTwinsIdentity from .digital_twins_patch_description import DigitalTwinsPatchDescription from .digital_twins_resource import DigitalTwinsResource from .error_definition import ErrorDefinition @@ -41,17 +59,36 @@ from .service_bus import ServiceBus from .event_hub import EventHub from .event_grid import EventGrid + from .group_id_information_properties import GroupIdInformationProperties + from .group_id_information_properties_model import GroupIdInformationPropertiesModel + from .group_id_information import GroupIdInformation + from .private_endpoint_connections_response import PrivateEndpointConnectionsResponse + from .group_id_information_response import GroupIdInformationResponse + from .connection_state import ConnectionState + from .private_endpoint import PrivateEndpoint + from .connection_properties_private_endpoint import ConnectionPropertiesPrivateEndpoint + from .connection_properties_private_link_service_connection_state import ConnectionPropertiesPrivateLinkServiceConnectionState + from .connection_properties import ConnectionProperties from .digital_twins_description_paged import DigitalTwinsDescriptionPaged from .digital_twins_endpoint_resource_paged import DigitalTwinsEndpointResourcePaged from .operation_paged import OperationPaged from .azure_digital_twins_management_client_enums import ( + PublicNetworkAccess, ProvisioningState, + DigitalTwinsIdentityType, Reason, EndpointProvisioningState, + AuthenticationType, + PrivateLinkServiceConnectionStatus, + ConnectionPropertiesProvisioningState, ) __all__ = [ + 'DigitalTwinsPatchProperties', + 'PrivateEndpointConnectionProperties', + 'PrivateEndpointConnection', 'DigitalTwinsDescription', + 'DigitalTwinsIdentity', 'DigitalTwinsPatchDescription', 'DigitalTwinsResource', 'ErrorDefinition', @@ -66,10 +103,25 @@ 'ServiceBus', 'EventHub', 'EventGrid', + 'GroupIdInformationProperties', + 'GroupIdInformationPropertiesModel', + 'GroupIdInformation', + 'PrivateEndpointConnectionsResponse', + 'GroupIdInformationResponse', + 'ConnectionState', + 'PrivateEndpoint', + 'ConnectionPropertiesPrivateEndpoint', + 'ConnectionPropertiesPrivateLinkServiceConnectionState', + 'ConnectionProperties', 'DigitalTwinsDescriptionPaged', 'DigitalTwinsEndpointResourcePaged', 'OperationPaged', + 'PublicNetworkAccess', 'ProvisioningState', + 'DigitalTwinsIdentityType', 'Reason', 'EndpointProvisioningState', + 'AuthenticationType', + 'PrivateLinkServiceConnectionStatus', + 'ConnectionPropertiesProvisioningState', ] diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/azure_digital_twins_management_client_enums.py b/azext_iot/sdk/digitaltwins/controlplane/models/azure_digital_twins_management_client_enums.py index ff6d2b607..c5cef2830 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/azure_digital_twins_management_client_enums.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/azure_digital_twins_management_client_enums.py @@ -12,10 +12,17 @@ from enum import Enum +class PublicNetworkAccess(str, Enum): + + enabled = "Enabled" + disabled = "Disabled" + + class ProvisioningState(str, Enum): provisioning = "Provisioning" deleting = "Deleting" + updating = "Updating" succeeded = "Succeeded" failed = "Failed" canceled = "Canceled" @@ -26,6 +33,12 @@ class ProvisioningState(str, Enum): moving = "Moving" +class DigitalTwinsIdentityType(str, Enum): + + none = "None" + system_assigned = "SystemAssigned" + + class Reason(str, Enum): invalid = "Invalid" @@ -45,3 +58,25 @@ class EndpointProvisioningState(str, Enum): restoring = "Restoring" moving = "Moving" disabled = "Disabled" + + +class AuthenticationType(str, Enum): + + key_based = "KeyBased" + identity_based = "IdentityBased" + + +class PrivateLinkServiceConnectionStatus(str, Enum): + + pending = "Pending" + approved = "Approved" + rejected = "Rejected" + disconnected = "Disconnected" + + +class ConnectionPropertiesProvisioningState(str, Enum): + + pending = "Pending" + approved = "Approved" + rejected = "Rejected" + disconnected = "Disconnected" diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/check_name_result.py b/azext_iot/sdk/digitaltwins/controlplane/models/check_name_result.py index 5fddb0bfe..bd2213e80 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/check_name_result.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/check_name_result.py @@ -23,7 +23,7 @@ class CheckNameResult(Model): :type message: str :param reason: Message providing the reason why the given name is invalid. Possible values include: 'Invalid', 'AlreadyExists' - :type reason: str or ~azure.mgmt.digitaltwins.models.Reason + :type reason: str or ~controlplane.models.Reason """ _attribute_map = { diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/check_name_result_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/check_name_result_py3.py index 7c2daedbd..76d83ffcf 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/check_name_result_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/check_name_result_py3.py @@ -23,7 +23,7 @@ class CheckNameResult(Model): :type message: str :param reason: Message providing the reason why the given name is invalid. Possible values include: 'Invalid', 'AlreadyExists' - :type reason: str or ~azure.mgmt.digitaltwins.models.Reason + :type reason: str or ~controlplane.models.Reason """ _attribute_map = { diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties.py b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties.py new file mode 100644 index 000000000..0fb707cf0 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties.py @@ -0,0 +1,52 @@ +# 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 msrest.serialization import Model + + +class ConnectionProperties(Model): + """The properties of a private endpoint connection. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar provisioning_state: The provisioning state. Possible values include: + 'Pending', 'Approved', 'Rejected', 'Disconnected' + :vartype provisioning_state: str or + ~controlplane.models.ConnectionPropertiesProvisioningState + :param private_endpoint: + :type private_endpoint: + ~controlplane.models.ConnectionPropertiesPrivateEndpoint + :param group_ids: The list of group ids for the private endpoint + connection. + :type group_ids: list[str] + :param private_link_service_connection_state: + :type private_link_service_connection_state: + ~controlplane.models.ConnectionPropertiesPrivateLinkServiceConnectionState + """ + + _validation = { + 'provisioning_state': {'readonly': True}, + } + + _attribute_map = { + 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, + 'private_endpoint': {'key': 'privateEndpoint', 'type': 'ConnectionPropertiesPrivateEndpoint'}, + 'group_ids': {'key': 'groupIds', 'type': '[str]'}, + 'private_link_service_connection_state': {'key': 'privateLinkServiceConnectionState', 'type': 'ConnectionPropertiesPrivateLinkServiceConnectionState'}, + } + + def __init__(self, **kwargs): + super(ConnectionProperties, self).__init__(**kwargs) + self.provisioning_state = None + self.private_endpoint = kwargs.get('private_endpoint', None) + self.group_ids = kwargs.get('group_ids', None) + self.private_link_service_connection_state = kwargs.get('private_link_service_connection_state', None) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_endpoint.py b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_endpoint.py new file mode 100644 index 000000000..3522e5561 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_endpoint.py @@ -0,0 +1,34 @@ +# 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 .private_endpoint import PrivateEndpoint + + +class ConnectionPropertiesPrivateEndpoint(PrivateEndpoint): + """ConnectionPropertiesPrivateEndpoint. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: The resource identifier. + :vartype id: str + """ + + _validation = { + 'id': {'readonly': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ConnectionPropertiesPrivateEndpoint, self).__init__(**kwargs) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_endpoint_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_endpoint_py3.py new file mode 100644 index 000000000..b3d1e2b29 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_endpoint_py3.py @@ -0,0 +1,34 @@ +# 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 .private_endpoint_py3 import PrivateEndpoint + + +class ConnectionPropertiesPrivateEndpoint(PrivateEndpoint): + """ConnectionPropertiesPrivateEndpoint. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: The resource identifier. + :vartype id: str + """ + + _validation = { + 'id': {'readonly': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + } + + def __init__(self, **kwargs) -> None: + super(ConnectionPropertiesPrivateEndpoint, self).__init__(**kwargs) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_link_service_connection_state.py b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_link_service_connection_state.py new file mode 100644 index 000000000..1d6a9870b --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_link_service_connection_state.py @@ -0,0 +1,44 @@ +# 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 .connection_state import ConnectionState + + +class ConnectionPropertiesPrivateLinkServiceConnectionState(ConnectionState): + """ConnectionPropertiesPrivateLinkServiceConnectionState. + + All required parameters must be populated in order to send to Azure. + + :param status: Required. The status of a private endpoint connection. + Possible values include: 'Pending', 'Approved', 'Rejected', 'Disconnected' + :type status: str or + ~controlplane.models.PrivateLinkServiceConnectionStatus + :param description: Required. The description for the current state of a + private endpoint connection. + :type description: str + :param actions_required: Actions required for a private endpoint + connection. + :type actions_required: str + """ + + _validation = { + 'status': {'required': True}, + 'description': {'required': True}, + } + + _attribute_map = { + 'status': {'key': 'status', 'type': 'str'}, + 'description': {'key': 'description', 'type': 'str'}, + 'actions_required': {'key': 'actionsRequired', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ConnectionPropertiesPrivateLinkServiceConnectionState, self).__init__(**kwargs) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_link_service_connection_state_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_link_service_connection_state_py3.py new file mode 100644 index 000000000..6e5964952 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_private_link_service_connection_state_py3.py @@ -0,0 +1,44 @@ +# 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 .connection_state_py3 import ConnectionState + + +class ConnectionPropertiesPrivateLinkServiceConnectionState(ConnectionState): + """ConnectionPropertiesPrivateLinkServiceConnectionState. + + All required parameters must be populated in order to send to Azure. + + :param status: Required. The status of a private endpoint connection. + Possible values include: 'Pending', 'Approved', 'Rejected', 'Disconnected' + :type status: str or + ~controlplane.models.PrivateLinkServiceConnectionStatus + :param description: Required. The description for the current state of a + private endpoint connection. + :type description: str + :param actions_required: Actions required for a private endpoint + connection. + :type actions_required: str + """ + + _validation = { + 'status': {'required': True}, + 'description': {'required': True}, + } + + _attribute_map = { + 'status': {'key': 'status', 'type': 'str'}, + 'description': {'key': 'description', 'type': 'str'}, + 'actions_required': {'key': 'actionsRequired', 'type': 'str'}, + } + + def __init__(self, *, status, description: str, actions_required: str=None, **kwargs) -> None: + super(ConnectionPropertiesPrivateLinkServiceConnectionState, self).__init__(status=status, description=description, actions_required=actions_required, **kwargs) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_py3.py new file mode 100644 index 000000000..25ef458e9 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/connection_properties_py3.py @@ -0,0 +1,52 @@ +# 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 msrest.serialization import Model + + +class ConnectionProperties(Model): + """The properties of a private endpoint connection. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar provisioning_state: The provisioning state. Possible values include: + 'Pending', 'Approved', 'Rejected', 'Disconnected' + :vartype provisioning_state: str or + ~controlplane.models.ConnectionPropertiesProvisioningState + :param private_endpoint: + :type private_endpoint: + ~controlplane.models.ConnectionPropertiesPrivateEndpoint + :param group_ids: The list of group ids for the private endpoint + connection. + :type group_ids: list[str] + :param private_link_service_connection_state: + :type private_link_service_connection_state: + ~controlplane.models.ConnectionPropertiesPrivateLinkServiceConnectionState + """ + + _validation = { + 'provisioning_state': {'readonly': True}, + } + + _attribute_map = { + 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, + 'private_endpoint': {'key': 'privateEndpoint', 'type': 'ConnectionPropertiesPrivateEndpoint'}, + 'group_ids': {'key': 'groupIds', 'type': '[str]'}, + 'private_link_service_connection_state': {'key': 'privateLinkServiceConnectionState', 'type': 'ConnectionPropertiesPrivateLinkServiceConnectionState'}, + } + + def __init__(self, *, private_endpoint=None, group_ids=None, private_link_service_connection_state=None, **kwargs) -> None: + super(ConnectionProperties, self).__init__(**kwargs) + self.provisioning_state = None + self.private_endpoint = private_endpoint + self.group_ids = group_ids + self.private_link_service_connection_state = private_link_service_connection_state diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/connection_state.py b/azext_iot/sdk/digitaltwins/controlplane/models/connection_state.py new file mode 100644 index 000000000..01ecfdc8a --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/connection_state.py @@ -0,0 +1,47 @@ +# 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 msrest.serialization import Model + + +class ConnectionState(Model): + """The current state of a private endpoint connection. + + All required parameters must be populated in order to send to Azure. + + :param status: Required. The status of a private endpoint connection. + Possible values include: 'Pending', 'Approved', 'Rejected', 'Disconnected' + :type status: str or + ~controlplane.models.PrivateLinkServiceConnectionStatus + :param description: Required. The description for the current state of a + private endpoint connection. + :type description: str + :param actions_required: Actions required for a private endpoint + connection. + :type actions_required: str + """ + + _validation = { + 'status': {'required': True}, + 'description': {'required': True}, + } + + _attribute_map = { + 'status': {'key': 'status', 'type': 'str'}, + 'description': {'key': 'description', 'type': 'str'}, + 'actions_required': {'key': 'actionsRequired', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(ConnectionState, self).__init__(**kwargs) + self.status = kwargs.get('status', None) + self.description = kwargs.get('description', None) + self.actions_required = kwargs.get('actions_required', None) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/connection_state_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/connection_state_py3.py new file mode 100644 index 000000000..938552448 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/connection_state_py3.py @@ -0,0 +1,47 @@ +# 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 msrest.serialization import Model + + +class ConnectionState(Model): + """The current state of a private endpoint connection. + + All required parameters must be populated in order to send to Azure. + + :param status: Required. The status of a private endpoint connection. + Possible values include: 'Pending', 'Approved', 'Rejected', 'Disconnected' + :type status: str or + ~controlplane.models.PrivateLinkServiceConnectionStatus + :param description: Required. The description for the current state of a + private endpoint connection. + :type description: str + :param actions_required: Actions required for a private endpoint + connection. + :type actions_required: str + """ + + _validation = { + 'status': {'required': True}, + 'description': {'required': True}, + } + + _attribute_map = { + 'status': {'key': 'status', 'type': 'str'}, + 'description': {'key': 'description', 'type': 'str'}, + 'actions_required': {'key': 'actionsRequired', 'type': 'str'}, + } + + def __init__(self, *, status, description: str, actions_required: str=None, **kwargs) -> None: + super(ConnectionState, self).__init__(**kwargs) + self.status = status + self.description = description + self.actions_required = actions_required diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description.py index 273c7e339..22e7d559b 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description.py @@ -30,17 +30,25 @@ class DigitalTwinsDescription(DigitalTwinsResource): :type location: str :param tags: The resource tags. :type tags: dict[str, str] + :param identity: The managed identity for the DigitalTwinsInstance. + :type identity: ~controlplane.models.DigitalTwinsIdentity :ivar created_time: Time when DigitalTwinsInstance was created. :vartype created_time: datetime :ivar last_updated_time: Time when DigitalTwinsInstance was updated. :vartype last_updated_time: datetime :ivar provisioning_state: The provisioning state. Possible values include: - 'Provisioning', 'Deleting', 'Succeeded', 'Failed', 'Canceled', 'Deleted', - 'Warning', 'Suspending', 'Restoring', 'Moving' - :vartype provisioning_state: str or - ~azure.mgmt.digitaltwins.models.ProvisioningState + 'Provisioning', 'Deleting', 'Updating', 'Succeeded', 'Failed', 'Canceled', + 'Deleted', 'Warning', 'Suspending', 'Restoring', 'Moving' + :vartype provisioning_state: str or ~controlplane.models.ProvisioningState :ivar host_name: Api endpoint to work with DigitalTwinsInstance. :vartype host_name: str + :param private_endpoint_connections: + :type private_endpoint_connections: + list[~controlplane.models.PrivateEndpointConnection] + :param public_network_access: Public network access for the + DigitalTwinsInstance. Possible values include: 'Enabled', 'Disabled' + :type public_network_access: str or + ~controlplane.models.PublicNetworkAccess """ _validation = { @@ -60,10 +68,13 @@ class DigitalTwinsDescription(DigitalTwinsResource): 'type': {'key': 'type', 'type': 'str'}, 'location': {'key': 'location', 'type': 'str'}, 'tags': {'key': 'tags', 'type': '{str}'}, + 'identity': {'key': 'identity', 'type': 'DigitalTwinsIdentity'}, 'created_time': {'key': 'properties.createdTime', 'type': 'iso-8601'}, 'last_updated_time': {'key': 'properties.lastUpdatedTime', 'type': 'iso-8601'}, 'provisioning_state': {'key': 'properties.provisioningState', 'type': 'str'}, 'host_name': {'key': 'properties.hostName', 'type': 'str'}, + 'private_endpoint_connections': {'key': 'properties.privateEndpointConnections', 'type': '[PrivateEndpointConnection]'}, + 'public_network_access': {'key': 'properties.publicNetworkAccess', 'type': 'str'}, } def __init__(self, **kwargs): @@ -72,3 +83,5 @@ def __init__(self, **kwargs): self.last_updated_time = None self.provisioning_state = None self.host_name = None + self.private_endpoint_connections = kwargs.get('private_endpoint_connections', None) + self.public_network_access = kwargs.get('public_network_access', None) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description_paged.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description_paged.py index c33257836..be83f1c8a 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description_paged.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description_paged.py @@ -14,7 +14,7 @@ class DigitalTwinsDescriptionPaged(Paged): """ - A paging container for iterating over a list of :class:`DigitalTwinsDescription ` object + A paging container for iterating over a list of :class:`DigitalTwinsDescription ` object """ _attribute_map = { diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description_py3.py index 648525f75..baeaccee7 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_description_py3.py @@ -30,17 +30,25 @@ class DigitalTwinsDescription(DigitalTwinsResource): :type location: str :param tags: The resource tags. :type tags: dict[str, str] + :param identity: The managed identity for the DigitalTwinsInstance. + :type identity: ~controlplane.models.DigitalTwinsIdentity :ivar created_time: Time when DigitalTwinsInstance was created. :vartype created_time: datetime :ivar last_updated_time: Time when DigitalTwinsInstance was updated. :vartype last_updated_time: datetime :ivar provisioning_state: The provisioning state. Possible values include: - 'Provisioning', 'Deleting', 'Succeeded', 'Failed', 'Canceled', 'Deleted', - 'Warning', 'Suspending', 'Restoring', 'Moving' - :vartype provisioning_state: str or - ~azure.mgmt.digitaltwins.models.ProvisioningState + 'Provisioning', 'Deleting', 'Updating', 'Succeeded', 'Failed', 'Canceled', + 'Deleted', 'Warning', 'Suspending', 'Restoring', 'Moving' + :vartype provisioning_state: str or ~controlplane.models.ProvisioningState :ivar host_name: Api endpoint to work with DigitalTwinsInstance. :vartype host_name: str + :param private_endpoint_connections: + :type private_endpoint_connections: + list[~controlplane.models.PrivateEndpointConnection] + :param public_network_access: Public network access for the + DigitalTwinsInstance. Possible values include: 'Enabled', 'Disabled' + :type public_network_access: str or + ~controlplane.models.PublicNetworkAccess """ _validation = { @@ -60,15 +68,20 @@ class DigitalTwinsDescription(DigitalTwinsResource): 'type': {'key': 'type', 'type': 'str'}, 'location': {'key': 'location', 'type': 'str'}, 'tags': {'key': 'tags', 'type': '{str}'}, + 'identity': {'key': 'identity', 'type': 'DigitalTwinsIdentity'}, 'created_time': {'key': 'properties.createdTime', 'type': 'iso-8601'}, 'last_updated_time': {'key': 'properties.lastUpdatedTime', 'type': 'iso-8601'}, 'provisioning_state': {'key': 'properties.provisioningState', 'type': 'str'}, 'host_name': {'key': 'properties.hostName', 'type': 'str'}, + 'private_endpoint_connections': {'key': 'properties.privateEndpointConnections', 'type': '[PrivateEndpointConnection]'}, + 'public_network_access': {'key': 'properties.publicNetworkAccess', 'type': 'str'}, } - def __init__(self, *, location: str, tags=None, **kwargs) -> None: - super(DigitalTwinsDescription, self).__init__(location=location, tags=tags, **kwargs) + def __init__(self, *, location: str, tags=None, identity=None, private_endpoint_connections=None, public_network_access=None, **kwargs) -> None: + super(DigitalTwinsDescription, self).__init__(location=location, tags=tags, identity=identity, **kwargs) self.created_time = None self.last_updated_time = None self.provisioning_state = None self.host_name = None + self.private_endpoint_connections = private_endpoint_connections + self.public_network_access = public_network_access diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource.py index 54b6809db..77f8d379d 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource.py @@ -29,7 +29,7 @@ class DigitalTwinsEndpointResource(ExternalResource): :param properties: Required. DigitalTwinsInstance endpoint resource properties. :type properties: - ~azure.mgmt.digitaltwins.models.DigitalTwinsEndpointResourceProperties + ~controlplane.models.DigitalTwinsEndpointResourceProperties """ _validation = { diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_paged.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_paged.py index 5673e29c2..c66d5f8c1 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_paged.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_paged.py @@ -14,7 +14,7 @@ class DigitalTwinsEndpointResourcePaged(Paged): """ - A paging container for iterating over a list of :class:`DigitalTwinsEndpointResource ` object + A paging container for iterating over a list of :class:`DigitalTwinsEndpointResource ` object """ _attribute_map = { diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_properties.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_properties.py index 1db0bbf80..f9bd90a60 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_properties.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_properties.py @@ -27,13 +27,20 @@ class DigitalTwinsEndpointResourceProperties(Model): 'Provisioning', 'Deleting', 'Succeeded', 'Failed', 'Canceled', 'Deleted', 'Warning', 'Suspending', 'Restoring', 'Moving', 'Disabled' :vartype provisioning_state: str or - ~azure.mgmt.digitaltwins.models.EndpointProvisioningState + ~controlplane.models.EndpointProvisioningState :ivar created_time: Time when the Endpoint was added to DigitalTwinsInstance. :vartype created_time: datetime - :param dead_letter_secret: Dead letter storage secret. Will be obfuscated - during read. + :param authentication_type: Specifies the authentication type being used + for connecting to the endpoint. Possible values include: 'KeyBased', + 'IdentityBased' + :type authentication_type: str or ~controlplane.models.AuthenticationType + :param dead_letter_secret: Dead letter storage secret for key-based + authentication. Will be obfuscated during read. :type dead_letter_secret: str + :param dead_letter_uri: Dead letter storage URL for identity-based + authentication. + :type dead_letter_uri: str :param endpoint_type: Required. Constant filled by server. :type endpoint_type: str """ @@ -47,7 +54,9 @@ class DigitalTwinsEndpointResourceProperties(Model): _attribute_map = { 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, 'created_time': {'key': 'createdTime', 'type': 'iso-8601'}, + 'authentication_type': {'key': 'authenticationType', 'type': 'str'}, 'dead_letter_secret': {'key': 'deadLetterSecret', 'type': 'str'}, + 'dead_letter_uri': {'key': 'deadLetterUri', 'type': 'str'}, 'endpoint_type': {'key': 'endpointType', 'type': 'str'}, } @@ -59,5 +68,7 @@ def __init__(self, **kwargs): super(DigitalTwinsEndpointResourceProperties, self).__init__(**kwargs) self.provisioning_state = None self.created_time = None + self.authentication_type = kwargs.get('authentication_type', None) self.dead_letter_secret = kwargs.get('dead_letter_secret', None) + self.dead_letter_uri = kwargs.get('dead_letter_uri', None) self.endpoint_type = None diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_properties_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_properties_py3.py index ef101f571..19a172fc1 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_properties_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_properties_py3.py @@ -27,13 +27,20 @@ class DigitalTwinsEndpointResourceProperties(Model): 'Provisioning', 'Deleting', 'Succeeded', 'Failed', 'Canceled', 'Deleted', 'Warning', 'Suspending', 'Restoring', 'Moving', 'Disabled' :vartype provisioning_state: str or - ~azure.mgmt.digitaltwins.models.EndpointProvisioningState + ~controlplane.models.EndpointProvisioningState :ivar created_time: Time when the Endpoint was added to DigitalTwinsInstance. :vartype created_time: datetime - :param dead_letter_secret: Dead letter storage secret. Will be obfuscated - during read. + :param authentication_type: Specifies the authentication type being used + for connecting to the endpoint. Possible values include: 'KeyBased', + 'IdentityBased' + :type authentication_type: str or ~controlplane.models.AuthenticationType + :param dead_letter_secret: Dead letter storage secret for key-based + authentication. Will be obfuscated during read. :type dead_letter_secret: str + :param dead_letter_uri: Dead letter storage URL for identity-based + authentication. + :type dead_letter_uri: str :param endpoint_type: Required. Constant filled by server. :type endpoint_type: str """ @@ -47,7 +54,9 @@ class DigitalTwinsEndpointResourceProperties(Model): _attribute_map = { 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, 'created_time': {'key': 'createdTime', 'type': 'iso-8601'}, + 'authentication_type': {'key': 'authenticationType', 'type': 'str'}, 'dead_letter_secret': {'key': 'deadLetterSecret', 'type': 'str'}, + 'dead_letter_uri': {'key': 'deadLetterUri', 'type': 'str'}, 'endpoint_type': {'key': 'endpointType', 'type': 'str'}, } @@ -55,9 +64,11 @@ class DigitalTwinsEndpointResourceProperties(Model): 'endpoint_type': {'ServiceBus': 'ServiceBus', 'EventHub': 'EventHub', 'EventGrid': 'EventGrid'} } - def __init__(self, *, dead_letter_secret: str=None, **kwargs) -> None: + def __init__(self, *, authentication_type=None, dead_letter_secret: str=None, dead_letter_uri: str=None, **kwargs) -> None: super(DigitalTwinsEndpointResourceProperties, self).__init__(**kwargs) self.provisioning_state = None self.created_time = None + self.authentication_type = authentication_type self.dead_letter_secret = dead_letter_secret + self.dead_letter_uri = dead_letter_uri self.endpoint_type = None diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_py3.py index 79def4503..bc3189328 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_endpoint_resource_py3.py @@ -29,7 +29,7 @@ class DigitalTwinsEndpointResource(ExternalResource): :param properties: Required. DigitalTwinsInstance endpoint resource properties. :type properties: - ~azure.mgmt.digitaltwins.models.DigitalTwinsEndpointResourceProperties + ~controlplane.models.DigitalTwinsEndpointResourceProperties """ _validation = { diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_identity.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_identity.py new file mode 100644 index 000000000..a7560f9cc --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_identity.py @@ -0,0 +1,50 @@ +# 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 msrest.serialization import Model + + +class DigitalTwinsIdentity(Model): + """The managed identity for the DigitalTwinsInstance. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :param type: The type of Managed Identity used by the + DigitalTwinsInstance. Only SystemAssigned is supported. Possible values + include: 'None', 'SystemAssigned' + :type type: str or ~controlplane.models.DigitalTwinsIdentityType + :ivar principal_id: The object id of the Managed Identity Resource. This + will be sent to the RP from ARM via the x-ms-identity-principal-id header + in the PUT request if the resource has a systemAssigned(implicit) identity + :vartype principal_id: str + :ivar tenant_id: The tenant id of the Managed Identity Resource. This will + be sent to the RP from ARM via the x-ms-client-tenant-id header in the PUT + request if the resource has a systemAssigned(implicit) identity + :vartype tenant_id: str + """ + + _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'}, + } + + def __init__(self, **kwargs): + super(DigitalTwinsIdentity, self).__init__(**kwargs) + self.type = kwargs.get('type', None) + self.principal_id = None + self.tenant_id = None diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_identity_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_identity_py3.py new file mode 100644 index 000000000..e2c7b19d6 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_identity_py3.py @@ -0,0 +1,50 @@ +# 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 msrest.serialization import Model + + +class DigitalTwinsIdentity(Model): + """The managed identity for the DigitalTwinsInstance. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :param type: The type of Managed Identity used by the + DigitalTwinsInstance. Only SystemAssigned is supported. Possible values + include: 'None', 'SystemAssigned' + :type type: str or ~controlplane.models.DigitalTwinsIdentityType + :ivar principal_id: The object id of the Managed Identity Resource. This + will be sent to the RP from ARM via the x-ms-identity-principal-id header + in the PUT request if the resource has a systemAssigned(implicit) identity + :vartype principal_id: str + :ivar tenant_id: The tenant id of the Managed Identity Resource. This will + be sent to the RP from ARM via the x-ms-client-tenant-id header in the PUT + request if the resource has a systemAssigned(implicit) identity + :vartype tenant_id: str + """ + + _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'}, + } + + def __init__(self, *, type=None, **kwargs) -> None: + super(DigitalTwinsIdentity, self).__init__(**kwargs) + self.type = type + self.principal_id = None + self.tenant_id = None diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_description.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_description.py index a9299bfa4..45eca2eab 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_description.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_description.py @@ -15,14 +15,22 @@ class DigitalTwinsPatchDescription(Model): """The description of the DigitalTwins service. - :param tags: Instance tags + :param tags: Instance patch properties :type tags: dict[str, str] + :param identity: The managed identity for the DigitalTwinsInstance. + :type identity: ~controlplane.models.DigitalTwinsIdentity + :param properties: Properties for the DigitalTwinsInstance. + :type properties: ~controlplane.models.DigitalTwinsPatchProperties """ _attribute_map = { 'tags': {'key': 'tags', 'type': '{str}'}, + 'identity': {'key': 'identity', 'type': 'DigitalTwinsIdentity'}, + 'properties': {'key': 'properties', 'type': 'DigitalTwinsPatchProperties'}, } def __init__(self, **kwargs): super(DigitalTwinsPatchDescription, self).__init__(**kwargs) self.tags = kwargs.get('tags', None) + self.identity = kwargs.get('identity', None) + self.properties = kwargs.get('properties', None) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_description_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_description_py3.py index a8e52a902..a4921909e 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_description_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_description_py3.py @@ -15,14 +15,22 @@ class DigitalTwinsPatchDescription(Model): """The description of the DigitalTwins service. - :param tags: Instance tags + :param tags: Instance patch properties :type tags: dict[str, str] + :param identity: The managed identity for the DigitalTwinsInstance. + :type identity: ~controlplane.models.DigitalTwinsIdentity + :param properties: Properties for the DigitalTwinsInstance. + :type properties: ~controlplane.models.DigitalTwinsPatchProperties """ _attribute_map = { 'tags': {'key': 'tags', 'type': '{str}'}, + 'identity': {'key': 'identity', 'type': 'DigitalTwinsIdentity'}, + 'properties': {'key': 'properties', 'type': 'DigitalTwinsPatchProperties'}, } - def __init__(self, *, tags=None, **kwargs) -> None: + def __init__(self, *, tags=None, identity=None, properties=None, **kwargs) -> None: super(DigitalTwinsPatchDescription, self).__init__(**kwargs) self.tags = tags + self.identity = identity + self.properties = properties diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_properties.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_properties.py new file mode 100644 index 000000000..9bee963ae --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_properties.py @@ -0,0 +1,30 @@ +# 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 msrest.serialization import Model + + +class DigitalTwinsPatchProperties(Model): + """The properties of a DigitalTwinsInstance. + + :param public_network_access: Public network access for the + DigitalTwinsInstance. Possible values include: 'Enabled', 'Disabled' + :type public_network_access: str or + ~controlplane.models.PublicNetworkAccess + """ + + _attribute_map = { + 'public_network_access': {'key': 'publicNetworkAccess', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(DigitalTwinsPatchProperties, self).__init__(**kwargs) + self.public_network_access = kwargs.get('public_network_access', None) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_properties_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_properties_py3.py new file mode 100644 index 000000000..3e356513b --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_patch_properties_py3.py @@ -0,0 +1,30 @@ +# 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 msrest.serialization import Model + + +class DigitalTwinsPatchProperties(Model): + """The properties of a DigitalTwinsInstance. + + :param public_network_access: Public network access for the + DigitalTwinsInstance. Possible values include: 'Enabled', 'Disabled' + :type public_network_access: str or + ~controlplane.models.PublicNetworkAccess + """ + + _attribute_map = { + 'public_network_access': {'key': 'publicNetworkAccess', 'type': 'str'}, + } + + def __init__(self, *, public_network_access=None, **kwargs) -> None: + super(DigitalTwinsPatchProperties, self).__init__(**kwargs) + self.public_network_access = public_network_access diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_resource.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_resource.py index f5b632031..08674430c 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_resource.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_resource.py @@ -30,6 +30,8 @@ class DigitalTwinsResource(Model): :type location: str :param tags: The resource tags. :type tags: dict[str, str] + :param identity: The managed identity for the DigitalTwinsInstance. + :type identity: ~controlplane.models.DigitalTwinsIdentity """ _validation = { @@ -45,6 +47,7 @@ class DigitalTwinsResource(Model): 'type': {'key': 'type', 'type': 'str'}, 'location': {'key': 'location', 'type': 'str'}, 'tags': {'key': 'tags', 'type': '{str}'}, + 'identity': {'key': 'identity', 'type': 'DigitalTwinsIdentity'}, } def __init__(self, **kwargs): @@ -54,3 +57,4 @@ def __init__(self, **kwargs): self.type = None self.location = kwargs.get('location', None) self.tags = kwargs.get('tags', None) + self.identity = kwargs.get('identity', None) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_resource_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_resource_py3.py index b3ea72049..a1c4b6051 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_resource_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/digital_twins_resource_py3.py @@ -30,6 +30,8 @@ class DigitalTwinsResource(Model): :type location: str :param tags: The resource tags. :type tags: dict[str, str] + :param identity: The managed identity for the DigitalTwinsInstance. + :type identity: ~controlplane.models.DigitalTwinsIdentity """ _validation = { @@ -45,12 +47,14 @@ class DigitalTwinsResource(Model): 'type': {'key': 'type', 'type': 'str'}, 'location': {'key': 'location', 'type': 'str'}, 'tags': {'key': 'tags', 'type': '{str}'}, + 'identity': {'key': 'identity', 'type': 'DigitalTwinsIdentity'}, } - def __init__(self, *, location: str, tags=None, **kwargs) -> None: + def __init__(self, *, location: str, tags=None, identity=None, **kwargs) -> None: super(DigitalTwinsResource, self).__init__(**kwargs) self.id = None self.name = None self.type = None self.location = location self.tags = tags + self.identity = identity diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/error_definition.py b/azext_iot/sdk/digitaltwins/controlplane/models/error_definition.py index c96f086b7..161802641 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/error_definition.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/error_definition.py @@ -24,7 +24,7 @@ class ErrorDefinition(Model): :ivar message: Description of the error. :vartype message: str :ivar details: Internal error details. - :vartype details: list[~azure.mgmt.digitaltwins.models.ErrorDefinition] + :vartype details: list[~controlplane.models.ErrorDefinition] """ _validation = { diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/error_definition_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/error_definition_py3.py index 8bb60ac87..09504ef69 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/error_definition_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/error_definition_py3.py @@ -24,7 +24,7 @@ class ErrorDefinition(Model): :ivar message: Description of the error. :vartype message: str :ivar details: Internal error details. - :vartype details: list[~azure.mgmt.digitaltwins.models.ErrorDefinition] + :vartype details: list[~controlplane.models.ErrorDefinition] """ _validation = { diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/error_response.py b/azext_iot/sdk/digitaltwins/controlplane/models/error_response.py index ea865540f..ddc297c39 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/error_response.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/error_response.py @@ -17,7 +17,7 @@ class ErrorResponse(Model): """Error response. :param error: Error description - :type error: ~azure.mgmt.digitaltwins.models.ErrorDefinition + :type error: ~controlplane.models.ErrorDefinition """ _attribute_map = { diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/error_response_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/error_response_py3.py index fef3c3115..2d9a3da48 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/error_response_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/error_response_py3.py @@ -17,7 +17,7 @@ class ErrorResponse(Model): """Error response. :param error: Error description - :type error: ~azure.mgmt.digitaltwins.models.ErrorDefinition + :type error: ~controlplane.models.ErrorDefinition """ _attribute_map = { diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/event_grid.py b/azext_iot/sdk/digitaltwins/controlplane/models/event_grid.py index 37bfbf98a..c2e176bed 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/event_grid.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/event_grid.py @@ -24,13 +24,20 @@ class EventGrid(DigitalTwinsEndpointResourceProperties): 'Provisioning', 'Deleting', 'Succeeded', 'Failed', 'Canceled', 'Deleted', 'Warning', 'Suspending', 'Restoring', 'Moving', 'Disabled' :vartype provisioning_state: str or - ~azure.mgmt.digitaltwins.models.EndpointProvisioningState + ~controlplane.models.EndpointProvisioningState :ivar created_time: Time when the Endpoint was added to DigitalTwinsInstance. :vartype created_time: datetime - :param dead_letter_secret: Dead letter storage secret. Will be obfuscated - during read. + :param authentication_type: Specifies the authentication type being used + for connecting to the endpoint. Possible values include: 'KeyBased', + 'IdentityBased' + :type authentication_type: str or ~controlplane.models.AuthenticationType + :param dead_letter_secret: Dead letter storage secret for key-based + authentication. Will be obfuscated during read. :type dead_letter_secret: str + :param dead_letter_uri: Dead letter storage URL for identity-based + authentication. + :type dead_letter_uri: str :param endpoint_type: Required. Constant filled by server. :type endpoint_type: str :param topic_endpoint: Required. EventGrid Topic Endpoint @@ -54,7 +61,9 @@ class EventGrid(DigitalTwinsEndpointResourceProperties): _attribute_map = { 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, 'created_time': {'key': 'createdTime', 'type': 'iso-8601'}, + 'authentication_type': {'key': 'authenticationType', 'type': 'str'}, 'dead_letter_secret': {'key': 'deadLetterSecret', 'type': 'str'}, + 'dead_letter_uri': {'key': 'deadLetterUri', 'type': 'str'}, 'endpoint_type': {'key': 'endpointType', 'type': 'str'}, 'topic_endpoint': {'key': 'TopicEndpoint', 'type': 'str'}, 'access_key1': {'key': 'accessKey1', 'type': 'str'}, diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/event_grid_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/event_grid_py3.py index 22674c4b3..5643ed6ed 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/event_grid_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/event_grid_py3.py @@ -24,13 +24,20 @@ class EventGrid(DigitalTwinsEndpointResourceProperties): 'Provisioning', 'Deleting', 'Succeeded', 'Failed', 'Canceled', 'Deleted', 'Warning', 'Suspending', 'Restoring', 'Moving', 'Disabled' :vartype provisioning_state: str or - ~azure.mgmt.digitaltwins.models.EndpointProvisioningState + ~controlplane.models.EndpointProvisioningState :ivar created_time: Time when the Endpoint was added to DigitalTwinsInstance. :vartype created_time: datetime - :param dead_letter_secret: Dead letter storage secret. Will be obfuscated - during read. + :param authentication_type: Specifies the authentication type being used + for connecting to the endpoint. Possible values include: 'KeyBased', + 'IdentityBased' + :type authentication_type: str or ~controlplane.models.AuthenticationType + :param dead_letter_secret: Dead letter storage secret for key-based + authentication. Will be obfuscated during read. :type dead_letter_secret: str + :param dead_letter_uri: Dead letter storage URL for identity-based + authentication. + :type dead_letter_uri: str :param endpoint_type: Required. Constant filled by server. :type endpoint_type: str :param topic_endpoint: Required. EventGrid Topic Endpoint @@ -54,15 +61,17 @@ class EventGrid(DigitalTwinsEndpointResourceProperties): _attribute_map = { 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, 'created_time': {'key': 'createdTime', 'type': 'iso-8601'}, + 'authentication_type': {'key': 'authenticationType', 'type': 'str'}, 'dead_letter_secret': {'key': 'deadLetterSecret', 'type': 'str'}, + 'dead_letter_uri': {'key': 'deadLetterUri', 'type': 'str'}, 'endpoint_type': {'key': 'endpointType', 'type': 'str'}, 'topic_endpoint': {'key': 'TopicEndpoint', 'type': 'str'}, 'access_key1': {'key': 'accessKey1', 'type': 'str'}, 'access_key2': {'key': 'accessKey2', 'type': 'str'}, } - def __init__(self, *, topic_endpoint: str, access_key1: str, dead_letter_secret: str=None, access_key2: str=None, **kwargs) -> None: - super(EventGrid, self).__init__(dead_letter_secret=dead_letter_secret, **kwargs) + def __init__(self, *, topic_endpoint: str, access_key1: str, authentication_type=None, dead_letter_secret: str=None, dead_letter_uri: str=None, access_key2: str=None, **kwargs) -> None: + super(EventGrid, self).__init__(authentication_type=authentication_type, dead_letter_secret=dead_letter_secret, dead_letter_uri=dead_letter_uri, **kwargs) self.topic_endpoint = topic_endpoint self.access_key1 = access_key1 self.access_key2 = access_key2 diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/event_hub.py b/azext_iot/sdk/digitaltwins/controlplane/models/event_hub.py index a3c70304c..2bb4e0584 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/event_hub.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/event_hub.py @@ -24,41 +24,59 @@ class EventHub(DigitalTwinsEndpointResourceProperties): 'Provisioning', 'Deleting', 'Succeeded', 'Failed', 'Canceled', 'Deleted', 'Warning', 'Suspending', 'Restoring', 'Moving', 'Disabled' :vartype provisioning_state: str or - ~azure.mgmt.digitaltwins.models.EndpointProvisioningState + ~controlplane.models.EndpointProvisioningState :ivar created_time: Time when the Endpoint was added to DigitalTwinsInstance. :vartype created_time: datetime - :param dead_letter_secret: Dead letter storage secret. Will be obfuscated - during read. + :param authentication_type: Specifies the authentication type being used + for connecting to the endpoint. Possible values include: 'KeyBased', + 'IdentityBased' + :type authentication_type: str or ~controlplane.models.AuthenticationType + :param dead_letter_secret: Dead letter storage secret for key-based + authentication. Will be obfuscated during read. :type dead_letter_secret: str + :param dead_letter_uri: Dead letter storage URL for identity-based + authentication. + :type dead_letter_uri: str :param endpoint_type: Required. Constant filled by server. :type endpoint_type: str - :param connection_string_primary_key: Required. PrimaryConnectionString of - the endpoint. Will be obfuscated during read. + :param connection_string_primary_key: PrimaryConnectionString of the + endpoint for key-based authentication. Will be obfuscated during read. :type connection_string_primary_key: str :param connection_string_secondary_key: SecondaryConnectionString of the - endpoint. Will be obfuscated during read. + endpoint for key-based authentication. Will be obfuscated during read. :type connection_string_secondary_key: str + :param endpoint_uri: The URL of the EventHub namespace for identity-based + authentication. It must include the protocol sb:// + :type endpoint_uri: str + :param entity_path: The EventHub name in the EventHub namespace for + identity-based authentication. + :type entity_path: str """ _validation = { 'provisioning_state': {'readonly': True}, 'created_time': {'readonly': True}, 'endpoint_type': {'required': True}, - 'connection_string_primary_key': {'required': True}, } _attribute_map = { 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, 'created_time': {'key': 'createdTime', 'type': 'iso-8601'}, + 'authentication_type': {'key': 'authenticationType', 'type': 'str'}, 'dead_letter_secret': {'key': 'deadLetterSecret', 'type': 'str'}, + 'dead_letter_uri': {'key': 'deadLetterUri', 'type': 'str'}, 'endpoint_type': {'key': 'endpointType', 'type': 'str'}, 'connection_string_primary_key': {'key': 'connectionStringPrimaryKey', 'type': 'str'}, 'connection_string_secondary_key': {'key': 'connectionStringSecondaryKey', 'type': 'str'}, + 'endpoint_uri': {'key': 'endpointUri', 'type': 'str'}, + 'entity_path': {'key': 'entityPath', 'type': 'str'}, } def __init__(self, **kwargs): super(EventHub, self).__init__(**kwargs) self.connection_string_primary_key = kwargs.get('connection_string_primary_key', None) self.connection_string_secondary_key = kwargs.get('connection_string_secondary_key', None) + self.endpoint_uri = kwargs.get('endpoint_uri', None) + self.entity_path = kwargs.get('entity_path', None) self.endpoint_type = 'EventHub' diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/event_hub_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/event_hub_py3.py index d76d794ec..8c22226af 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/event_hub_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/event_hub_py3.py @@ -24,41 +24,59 @@ class EventHub(DigitalTwinsEndpointResourceProperties): 'Provisioning', 'Deleting', 'Succeeded', 'Failed', 'Canceled', 'Deleted', 'Warning', 'Suspending', 'Restoring', 'Moving', 'Disabled' :vartype provisioning_state: str or - ~azure.mgmt.digitaltwins.models.EndpointProvisioningState + ~controlplane.models.EndpointProvisioningState :ivar created_time: Time when the Endpoint was added to DigitalTwinsInstance. :vartype created_time: datetime - :param dead_letter_secret: Dead letter storage secret. Will be obfuscated - during read. + :param authentication_type: Specifies the authentication type being used + for connecting to the endpoint. Possible values include: 'KeyBased', + 'IdentityBased' + :type authentication_type: str or ~controlplane.models.AuthenticationType + :param dead_letter_secret: Dead letter storage secret for key-based + authentication. Will be obfuscated during read. :type dead_letter_secret: str + :param dead_letter_uri: Dead letter storage URL for identity-based + authentication. + :type dead_letter_uri: str :param endpoint_type: Required. Constant filled by server. :type endpoint_type: str - :param connection_string_primary_key: Required. PrimaryConnectionString of - the endpoint. Will be obfuscated during read. + :param connection_string_primary_key: PrimaryConnectionString of the + endpoint for key-based authentication. Will be obfuscated during read. :type connection_string_primary_key: str :param connection_string_secondary_key: SecondaryConnectionString of the - endpoint. Will be obfuscated during read. + endpoint for key-based authentication. Will be obfuscated during read. :type connection_string_secondary_key: str + :param endpoint_uri: The URL of the EventHub namespace for identity-based + authentication. It must include the protocol sb:// + :type endpoint_uri: str + :param entity_path: The EventHub name in the EventHub namespace for + identity-based authentication. + :type entity_path: str """ _validation = { 'provisioning_state': {'readonly': True}, 'created_time': {'readonly': True}, 'endpoint_type': {'required': True}, - 'connection_string_primary_key': {'required': True}, } _attribute_map = { 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, 'created_time': {'key': 'createdTime', 'type': 'iso-8601'}, + 'authentication_type': {'key': 'authenticationType', 'type': 'str'}, 'dead_letter_secret': {'key': 'deadLetterSecret', 'type': 'str'}, + 'dead_letter_uri': {'key': 'deadLetterUri', 'type': 'str'}, 'endpoint_type': {'key': 'endpointType', 'type': 'str'}, 'connection_string_primary_key': {'key': 'connectionStringPrimaryKey', 'type': 'str'}, 'connection_string_secondary_key': {'key': 'connectionStringSecondaryKey', 'type': 'str'}, + 'endpoint_uri': {'key': 'endpointUri', 'type': 'str'}, + 'entity_path': {'key': 'entityPath', 'type': 'str'}, } - def __init__(self, *, connection_string_primary_key: str, dead_letter_secret: str=None, connection_string_secondary_key: str=None, **kwargs) -> None: - super(EventHub, self).__init__(dead_letter_secret=dead_letter_secret, **kwargs) + def __init__(self, *, authentication_type=None, dead_letter_secret: str=None, dead_letter_uri: str=None, connection_string_primary_key: str=None, connection_string_secondary_key: str=None, endpoint_uri: str=None, entity_path: str=None, **kwargs) -> None: + super(EventHub, self).__init__(authentication_type=authentication_type, dead_letter_secret=dead_letter_secret, dead_letter_uri=dead_letter_uri, **kwargs) self.connection_string_primary_key = connection_string_primary_key self.connection_string_secondary_key = connection_string_secondary_key + self.endpoint_uri = endpoint_uri + self.entity_path = entity_path self.endpoint_type = 'EventHub' diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information.py b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information.py new file mode 100644 index 000000000..ac990009c --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information.py @@ -0,0 +1,51 @@ +# 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 msrest.serialization import Model + + +class GroupIdInformation(Model): + """The group information for creating a private endpoint on Digital Twin. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :param properties: Required. + :type properties: ~controlplane.models.GroupIdInformationPropertiesModel + :param id: The resource identifier. + :type id: str + :ivar name: The resource name. + :vartype name: str + :ivar type: The resource type. + :vartype type: str + """ + + _validation = { + 'properties': {'required': True}, + 'name': {'readonly': True, 'pattern': r'^(?![0-9]+$)(?!-)[a-zA-Z0-9-]{2,49}[a-zA-Z0-9]$'}, + 'type': {'readonly': True}, + } + + _attribute_map = { + 'properties': {'key': 'properties', 'type': 'GroupIdInformationPropertiesModel'}, + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(GroupIdInformation, self).__init__(**kwargs) + self.properties = kwargs.get('properties', None) + self.id = kwargs.get('id', None) + self.name = None + self.type = None diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties.py b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties.py new file mode 100644 index 000000000..1398514fb --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties.py @@ -0,0 +1,37 @@ +# 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 msrest.serialization import Model + + +class GroupIdInformationProperties(Model): + """The properties for a group information object. + + :param group_id: The group id + :type group_id: str + :param required_members: The required members for a specific group id. + :type required_members: list[str] + :param required_zone_names: The required DNS zones for a specific group + id. + :type required_zone_names: list[str] + """ + + _attribute_map = { + 'group_id': {'key': 'groupId', 'type': 'str'}, + 'required_members': {'key': 'requiredMembers', 'type': '[str]'}, + 'required_zone_names': {'key': 'requiredZoneNames', 'type': '[str]'}, + } + + def __init__(self, **kwargs): + super(GroupIdInformationProperties, self).__init__(**kwargs) + self.group_id = kwargs.get('group_id', None) + self.required_members = kwargs.get('required_members', None) + self.required_zone_names = kwargs.get('required_zone_names', None) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_model.py b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_model.py new file mode 100644 index 000000000..a60bfad30 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_model.py @@ -0,0 +1,34 @@ +# 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 .group_id_information_properties import GroupIdInformationProperties + + +class GroupIdInformationPropertiesModel(GroupIdInformationProperties): + """GroupIdInformationPropertiesModel. + + :param group_id: The group id + :type group_id: str + :param required_members: The required members for a specific group id. + :type required_members: list[str] + :param required_zone_names: The required DNS zones for a specific group + id. + :type required_zone_names: list[str] + """ + + _attribute_map = { + 'group_id': {'key': 'groupId', 'type': 'str'}, + 'required_members': {'key': 'requiredMembers', 'type': '[str]'}, + 'required_zone_names': {'key': 'requiredZoneNames', 'type': '[str]'}, + } + + def __init__(self, **kwargs): + super(GroupIdInformationPropertiesModel, self).__init__(**kwargs) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_model_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_model_py3.py new file mode 100644 index 000000000..b472ea7a6 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_model_py3.py @@ -0,0 +1,34 @@ +# 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 .group_id_information_properties_py3 import GroupIdInformationProperties + + +class GroupIdInformationPropertiesModel(GroupIdInformationProperties): + """GroupIdInformationPropertiesModel. + + :param group_id: The group id + :type group_id: str + :param required_members: The required members for a specific group id. + :type required_members: list[str] + :param required_zone_names: The required DNS zones for a specific group + id. + :type required_zone_names: list[str] + """ + + _attribute_map = { + 'group_id': {'key': 'groupId', 'type': 'str'}, + 'required_members': {'key': 'requiredMembers', 'type': '[str]'}, + 'required_zone_names': {'key': 'requiredZoneNames', 'type': '[str]'}, + } + + def __init__(self, *, group_id: str=None, required_members=None, required_zone_names=None, **kwargs) -> None: + super(GroupIdInformationPropertiesModel, self).__init__(group_id=group_id, required_members=required_members, required_zone_names=required_zone_names, **kwargs) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_py3.py new file mode 100644 index 000000000..3e44ba4a5 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_properties_py3.py @@ -0,0 +1,37 @@ +# 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 msrest.serialization import Model + + +class GroupIdInformationProperties(Model): + """The properties for a group information object. + + :param group_id: The group id + :type group_id: str + :param required_members: The required members for a specific group id. + :type required_members: list[str] + :param required_zone_names: The required DNS zones for a specific group + id. + :type required_zone_names: list[str] + """ + + _attribute_map = { + 'group_id': {'key': 'groupId', 'type': 'str'}, + 'required_members': {'key': 'requiredMembers', 'type': '[str]'}, + 'required_zone_names': {'key': 'requiredZoneNames', 'type': '[str]'}, + } + + def __init__(self, *, group_id: str=None, required_members=None, required_zone_names=None, **kwargs) -> None: + super(GroupIdInformationProperties, self).__init__(**kwargs) + self.group_id = group_id + self.required_members = required_members + self.required_zone_names = required_zone_names diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_py3.py new file mode 100644 index 000000000..4574232c2 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_py3.py @@ -0,0 +1,51 @@ +# 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 msrest.serialization import Model + + +class GroupIdInformation(Model): + """The group information for creating a private endpoint on Digital Twin. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :param properties: Required. + :type properties: ~controlplane.models.GroupIdInformationPropertiesModel + :param id: The resource identifier. + :type id: str + :ivar name: The resource name. + :vartype name: str + :ivar type: The resource type. + :vartype type: str + """ + + _validation = { + 'properties': {'required': True}, + 'name': {'readonly': True, 'pattern': r'^(?![0-9]+$)(?!-)[a-zA-Z0-9-]{2,49}[a-zA-Z0-9]$'}, + 'type': {'readonly': True}, + } + + _attribute_map = { + 'properties': {'key': 'properties', 'type': 'GroupIdInformationPropertiesModel'}, + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + } + + def __init__(self, *, properties, id: str=None, **kwargs) -> None: + super(GroupIdInformation, self).__init__(**kwargs) + self.properties = properties + self.id = id + self.name = None + self.type = None diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_response.py b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_response.py new file mode 100644 index 000000000..09c4dd92c --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_response.py @@ -0,0 +1,29 @@ +# 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 msrest.serialization import Model + + +class GroupIdInformationResponse(Model): + """The available private link resources for a Digital Twin. + + :param value: The list of available private link resources for a Digital + Twin. + :type value: list[~controlplane.models.GroupIdInformation] + """ + + _attribute_map = { + 'value': {'key': 'value', 'type': '[GroupIdInformation]'}, + } + + def __init__(self, **kwargs): + super(GroupIdInformationResponse, self).__init__(**kwargs) + self.value = kwargs.get('value', None) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_response_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_response_py3.py new file mode 100644 index 000000000..0ae4024a9 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/group_id_information_response_py3.py @@ -0,0 +1,29 @@ +# 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 msrest.serialization import Model + + +class GroupIdInformationResponse(Model): + """The available private link resources for a Digital Twin. + + :param value: The list of available private link resources for a Digital + Twin. + :type value: list[~controlplane.models.GroupIdInformation] + """ + + _attribute_map = { + 'value': {'key': 'value', 'type': '[GroupIdInformation]'}, + } + + def __init__(self, *, value=None, **kwargs) -> None: + super(GroupIdInformationResponse, self).__init__(**kwargs) + self.value = value diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/operation.py b/azext_iot/sdk/digitaltwins/controlplane/models/operation.py index 71aafcc50..234ed09af 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/operation.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/operation.py @@ -22,7 +22,7 @@ class Operation(Model): delete} :vartype name: str :param display: Operation properties display - :type display: ~azure.mgmt.digitaltwins.models.OperationDisplay + :type display: ~controlplane.models.OperationDisplay :ivar origin: The intended executor of the operation. :vartype origin: str :ivar is_data_action: If the operation is a data action (for data plane diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/operation_paged.py b/azext_iot/sdk/digitaltwins/controlplane/models/operation_paged.py index ae64e3a7d..ff83d7bef 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/operation_paged.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/operation_paged.py @@ -14,7 +14,7 @@ class OperationPaged(Paged): """ - A paging container for iterating over a list of :class:`Operation ` object + A paging container for iterating over a list of :class:`Operation ` object """ _attribute_map = { diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/operation_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/operation_py3.py index f87e6da83..c60799393 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/operation_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/operation_py3.py @@ -22,7 +22,7 @@ class Operation(Model): delete} :vartype name: str :param display: Operation properties display - :type display: ~azure.mgmt.digitaltwins.models.OperationDisplay + :type display: ~controlplane.models.OperationDisplay :ivar origin: The intended executor of the operation. :vartype origin: str :ivar is_data_action: If the operation is a data action (for data plane diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint.py b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint.py new file mode 100644 index 000000000..4cebb4acc --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint.py @@ -0,0 +1,35 @@ +# 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 msrest.serialization import Model + + +class PrivateEndpoint(Model): + """The private endpoint property of a private endpoint connection. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: The resource identifier. + :vartype id: str + """ + + _validation = { + 'id': {'readonly': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + } + + def __init__(self, **kwargs): + super(PrivateEndpoint, self).__init__(**kwargs) + self.id = None diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection.py b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection.py new file mode 100644 index 000000000..085f56fbc --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection.py @@ -0,0 +1,52 @@ +# 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 msrest.serialization import Model + + +class PrivateEndpointConnection(Model): + """The private endpoint connection of a Digital Twin. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar id: The resource identifier. + :vartype id: str + :ivar name: The resource name. + :vartype name: str + :ivar type: The resource type. + :vartype type: str + :param properties: Required. + :type properties: ~controlplane.models.PrivateEndpointConnectionProperties + """ + + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True, 'pattern': r'^(?![0-9]+$)(?!-)[a-zA-Z0-9-]{2,49}[a-zA-Z0-9]$'}, + 'type': {'readonly': True}, + 'properties': {'required': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + 'properties': {'key': 'properties', 'type': 'PrivateEndpointConnectionProperties'}, + } + + def __init__(self, **kwargs): + super(PrivateEndpointConnection, self).__init__(**kwargs) + self.id = None + self.name = None + self.type = None + self.properties = kwargs.get('properties', None) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_properties.py b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_properties.py new file mode 100644 index 000000000..9ef003a5a --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_properties.py @@ -0,0 +1,48 @@ +# 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 .connection_properties import ConnectionProperties + + +class PrivateEndpointConnectionProperties(ConnectionProperties): + """PrivateEndpointConnectionProperties. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar provisioning_state: The provisioning state. Possible values include: + 'Pending', 'Approved', 'Rejected', 'Disconnected' + :vartype provisioning_state: str or + ~controlplane.models.ConnectionPropertiesProvisioningState + :param private_endpoint: + :type private_endpoint: + ~controlplane.models.ConnectionPropertiesPrivateEndpoint + :param group_ids: The list of group ids for the private endpoint + connection. + :type group_ids: list[str] + :param private_link_service_connection_state: + :type private_link_service_connection_state: + ~controlplane.models.ConnectionPropertiesPrivateLinkServiceConnectionState + """ + + _validation = { + 'provisioning_state': {'readonly': True}, + } + + _attribute_map = { + 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, + 'private_endpoint': {'key': 'privateEndpoint', 'type': 'ConnectionPropertiesPrivateEndpoint'}, + 'group_ids': {'key': 'groupIds', 'type': '[str]'}, + 'private_link_service_connection_state': {'key': 'privateLinkServiceConnectionState', 'type': 'ConnectionPropertiesPrivateLinkServiceConnectionState'}, + } + + def __init__(self, **kwargs): + super(PrivateEndpointConnectionProperties, self).__init__(**kwargs) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_properties_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_properties_py3.py new file mode 100644 index 000000000..b1b829ca4 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_properties_py3.py @@ -0,0 +1,48 @@ +# 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 .connection_properties_py3 import ConnectionProperties + + +class PrivateEndpointConnectionProperties(ConnectionProperties): + """PrivateEndpointConnectionProperties. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar provisioning_state: The provisioning state. Possible values include: + 'Pending', 'Approved', 'Rejected', 'Disconnected' + :vartype provisioning_state: str or + ~controlplane.models.ConnectionPropertiesProvisioningState + :param private_endpoint: + :type private_endpoint: + ~controlplane.models.ConnectionPropertiesPrivateEndpoint + :param group_ids: The list of group ids for the private endpoint + connection. + :type group_ids: list[str] + :param private_link_service_connection_state: + :type private_link_service_connection_state: + ~controlplane.models.ConnectionPropertiesPrivateLinkServiceConnectionState + """ + + _validation = { + 'provisioning_state': {'readonly': True}, + } + + _attribute_map = { + 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, + 'private_endpoint': {'key': 'privateEndpoint', 'type': 'ConnectionPropertiesPrivateEndpoint'}, + 'group_ids': {'key': 'groupIds', 'type': '[str]'}, + 'private_link_service_connection_state': {'key': 'privateLinkServiceConnectionState', 'type': 'ConnectionPropertiesPrivateLinkServiceConnectionState'}, + } + + def __init__(self, *, private_endpoint=None, group_ids=None, private_link_service_connection_state=None, **kwargs) -> None: + super(PrivateEndpointConnectionProperties, self).__init__(private_endpoint=private_endpoint, group_ids=group_ids, private_link_service_connection_state=private_link_service_connection_state, **kwargs) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_py3.py new file mode 100644 index 000000000..936c84d9d --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connection_py3.py @@ -0,0 +1,52 @@ +# 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 msrest.serialization import Model + + +class PrivateEndpointConnection(Model): + """The private endpoint connection of a Digital Twin. + + Variables are only populated by the server, and will be ignored when + sending a request. + + All required parameters must be populated in order to send to Azure. + + :ivar id: The resource identifier. + :vartype id: str + :ivar name: The resource name. + :vartype name: str + :ivar type: The resource type. + :vartype type: str + :param properties: Required. + :type properties: ~controlplane.models.PrivateEndpointConnectionProperties + """ + + _validation = { + 'id': {'readonly': True}, + 'name': {'readonly': True, 'pattern': r'^(?![0-9]+$)(?!-)[a-zA-Z0-9-]{2,49}[a-zA-Z0-9]$'}, + 'type': {'readonly': True}, + 'properties': {'required': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + 'name': {'key': 'name', 'type': 'str'}, + 'type': {'key': 'type', 'type': 'str'}, + 'properties': {'key': 'properties', 'type': 'PrivateEndpointConnectionProperties'}, + } + + def __init__(self, *, properties, **kwargs) -> None: + super(PrivateEndpointConnection, self).__init__(**kwargs) + self.id = None + self.name = None + self.type = None + self.properties = properties diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connections_response.py b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connections_response.py new file mode 100644 index 000000000..cb53dc315 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connections_response.py @@ -0,0 +1,29 @@ +# 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 msrest.serialization import Model + + +class PrivateEndpointConnectionsResponse(Model): + """The available private link connections for a Digital Twin. + + :param value: The list of available private link connections for a Digital + Twin. + :type value: list[~controlplane.models.PrivateEndpointConnection] + """ + + _attribute_map = { + 'value': {'key': 'value', 'type': '[PrivateEndpointConnection]'}, + } + + def __init__(self, **kwargs): + super(PrivateEndpointConnectionsResponse, self).__init__(**kwargs) + self.value = kwargs.get('value', None) diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connections_response_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connections_response_py3.py new file mode 100644 index 000000000..51197678c --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_connections_response_py3.py @@ -0,0 +1,29 @@ +# 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 msrest.serialization import Model + + +class PrivateEndpointConnectionsResponse(Model): + """The available private link connections for a Digital Twin. + + :param value: The list of available private link connections for a Digital + Twin. + :type value: list[~controlplane.models.PrivateEndpointConnection] + """ + + _attribute_map = { + 'value': {'key': 'value', 'type': '[PrivateEndpointConnection]'}, + } + + def __init__(self, *, value=None, **kwargs) -> None: + super(PrivateEndpointConnectionsResponse, self).__init__(**kwargs) + self.value = value diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_py3.py new file mode 100644 index 000000000..4faffd817 --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/models/private_endpoint_py3.py @@ -0,0 +1,35 @@ +# 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 msrest.serialization import Model + + +class PrivateEndpoint(Model): + """The private endpoint property of a private endpoint connection. + + Variables are only populated by the server, and will be ignored when + sending a request. + + :ivar id: The resource identifier. + :vartype id: str + """ + + _validation = { + 'id': {'readonly': True}, + } + + _attribute_map = { + 'id': {'key': 'id', 'type': 'str'}, + } + + def __init__(self, **kwargs) -> None: + super(PrivateEndpoint, self).__init__(**kwargs) + self.id = None diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/service_bus.py b/azext_iot/sdk/digitaltwins/controlplane/models/service_bus.py index 033ec34e8..261bb07d5 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/service_bus.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/service_bus.py @@ -24,41 +24,59 @@ class ServiceBus(DigitalTwinsEndpointResourceProperties): 'Provisioning', 'Deleting', 'Succeeded', 'Failed', 'Canceled', 'Deleted', 'Warning', 'Suspending', 'Restoring', 'Moving', 'Disabled' :vartype provisioning_state: str or - ~azure.mgmt.digitaltwins.models.EndpointProvisioningState + ~controlplane.models.EndpointProvisioningState :ivar created_time: Time when the Endpoint was added to DigitalTwinsInstance. :vartype created_time: datetime - :param dead_letter_secret: Dead letter storage secret. Will be obfuscated - during read. + :param authentication_type: Specifies the authentication type being used + for connecting to the endpoint. Possible values include: 'KeyBased', + 'IdentityBased' + :type authentication_type: str or ~controlplane.models.AuthenticationType + :param dead_letter_secret: Dead letter storage secret for key-based + authentication. Will be obfuscated during read. :type dead_letter_secret: str + :param dead_letter_uri: Dead letter storage URL for identity-based + authentication. + :type dead_letter_uri: str :param endpoint_type: Required. Constant filled by server. :type endpoint_type: str - :param primary_connection_string: Required. PrimaryConnectionString of the - endpoint. Will be obfuscated during read. + :param primary_connection_string: PrimaryConnectionString of the endpoint + for key-based authentication. Will be obfuscated during read. :type primary_connection_string: str :param secondary_connection_string: SecondaryConnectionString of the - endpoint. Will be obfuscated during read. + endpoint for key-based authentication. Will be obfuscated during read. :type secondary_connection_string: str + :param endpoint_uri: The URL of the ServiceBus namespace for + identity-based authentication. It must include the protocol sb:// + :type endpoint_uri: str + :param entity_path: The ServiceBus Topic name for identity-based + authentication + :type entity_path: str """ _validation = { 'provisioning_state': {'readonly': True}, 'created_time': {'readonly': True}, 'endpoint_type': {'required': True}, - 'primary_connection_string': {'required': True}, } _attribute_map = { 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, 'created_time': {'key': 'createdTime', 'type': 'iso-8601'}, + 'authentication_type': {'key': 'authenticationType', 'type': 'str'}, 'dead_letter_secret': {'key': 'deadLetterSecret', 'type': 'str'}, + 'dead_letter_uri': {'key': 'deadLetterUri', 'type': 'str'}, 'endpoint_type': {'key': 'endpointType', 'type': 'str'}, 'primary_connection_string': {'key': 'primaryConnectionString', 'type': 'str'}, 'secondary_connection_string': {'key': 'secondaryConnectionString', 'type': 'str'}, + 'endpoint_uri': {'key': 'endpointUri', 'type': 'str'}, + 'entity_path': {'key': 'entityPath', 'type': 'str'}, } def __init__(self, **kwargs): super(ServiceBus, self).__init__(**kwargs) self.primary_connection_string = kwargs.get('primary_connection_string', None) self.secondary_connection_string = kwargs.get('secondary_connection_string', None) + self.endpoint_uri = kwargs.get('endpoint_uri', None) + self.entity_path = kwargs.get('entity_path', None) self.endpoint_type = 'ServiceBus' diff --git a/azext_iot/sdk/digitaltwins/controlplane/models/service_bus_py3.py b/azext_iot/sdk/digitaltwins/controlplane/models/service_bus_py3.py index 80ad6758f..30782f1c0 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/models/service_bus_py3.py +++ b/azext_iot/sdk/digitaltwins/controlplane/models/service_bus_py3.py @@ -24,41 +24,59 @@ class ServiceBus(DigitalTwinsEndpointResourceProperties): 'Provisioning', 'Deleting', 'Succeeded', 'Failed', 'Canceled', 'Deleted', 'Warning', 'Suspending', 'Restoring', 'Moving', 'Disabled' :vartype provisioning_state: str or - ~azure.mgmt.digitaltwins.models.EndpointProvisioningState + ~controlplane.models.EndpointProvisioningState :ivar created_time: Time when the Endpoint was added to DigitalTwinsInstance. :vartype created_time: datetime - :param dead_letter_secret: Dead letter storage secret. Will be obfuscated - during read. + :param authentication_type: Specifies the authentication type being used + for connecting to the endpoint. Possible values include: 'KeyBased', + 'IdentityBased' + :type authentication_type: str or ~controlplane.models.AuthenticationType + :param dead_letter_secret: Dead letter storage secret for key-based + authentication. Will be obfuscated during read. :type dead_letter_secret: str + :param dead_letter_uri: Dead letter storage URL for identity-based + authentication. + :type dead_letter_uri: str :param endpoint_type: Required. Constant filled by server. :type endpoint_type: str - :param primary_connection_string: Required. PrimaryConnectionString of the - endpoint. Will be obfuscated during read. + :param primary_connection_string: PrimaryConnectionString of the endpoint + for key-based authentication. Will be obfuscated during read. :type primary_connection_string: str :param secondary_connection_string: SecondaryConnectionString of the - endpoint. Will be obfuscated during read. + endpoint for key-based authentication. Will be obfuscated during read. :type secondary_connection_string: str + :param endpoint_uri: The URL of the ServiceBus namespace for + identity-based authentication. It must include the protocol sb:// + :type endpoint_uri: str + :param entity_path: The ServiceBus Topic name for identity-based + authentication + :type entity_path: str """ _validation = { 'provisioning_state': {'readonly': True}, 'created_time': {'readonly': True}, 'endpoint_type': {'required': True}, - 'primary_connection_string': {'required': True}, } _attribute_map = { 'provisioning_state': {'key': 'provisioningState', 'type': 'str'}, 'created_time': {'key': 'createdTime', 'type': 'iso-8601'}, + 'authentication_type': {'key': 'authenticationType', 'type': 'str'}, 'dead_letter_secret': {'key': 'deadLetterSecret', 'type': 'str'}, + 'dead_letter_uri': {'key': 'deadLetterUri', 'type': 'str'}, 'endpoint_type': {'key': 'endpointType', 'type': 'str'}, 'primary_connection_string': {'key': 'primaryConnectionString', 'type': 'str'}, 'secondary_connection_string': {'key': 'secondaryConnectionString', 'type': 'str'}, + 'endpoint_uri': {'key': 'endpointUri', 'type': 'str'}, + 'entity_path': {'key': 'entityPath', 'type': 'str'}, } - def __init__(self, *, primary_connection_string: str, dead_letter_secret: str=None, secondary_connection_string: str=None, **kwargs) -> None: - super(ServiceBus, self).__init__(dead_letter_secret=dead_letter_secret, **kwargs) + def __init__(self, *, authentication_type=None, dead_letter_secret: str=None, dead_letter_uri: str=None, primary_connection_string: str=None, secondary_connection_string: str=None, endpoint_uri: str=None, entity_path: str=None, **kwargs) -> None: + super(ServiceBus, self).__init__(authentication_type=authentication_type, dead_letter_secret=dead_letter_secret, dead_letter_uri=dead_letter_uri, **kwargs) self.primary_connection_string = primary_connection_string self.secondary_connection_string = secondary_connection_string + self.endpoint_uri = endpoint_uri + self.entity_path = entity_path self.endpoint_type = 'ServiceBus' diff --git a/azext_iot/sdk/digitaltwins/controlplane/operations/__init__.py b/azext_iot/sdk/digitaltwins/controlplane/operations/__init__.py index d55a7f121..00f62af04 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/operations/__init__.py +++ b/azext_iot/sdk/digitaltwins/controlplane/operations/__init__.py @@ -12,9 +12,13 @@ from .digital_twins_operations import DigitalTwinsOperations from .digital_twins_endpoint_operations import DigitalTwinsEndpointOperations from .operations import Operations +from .private_link_resources_operations import PrivateLinkResourcesOperations +from .private_endpoint_connections_operations import PrivateEndpointConnectionsOperations __all__ = [ 'DigitalTwinsOperations', 'DigitalTwinsEndpointOperations', 'Operations', + 'PrivateLinkResourcesOperations', + 'PrivateEndpointConnectionsOperations', ] diff --git a/azext_iot/sdk/digitaltwins/controlplane/operations/digital_twins_endpoint_operations.py b/azext_iot/sdk/digitaltwins/controlplane/operations/digital_twins_endpoint_operations.py index e5b62cefb..e6bbb2bc2 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/operations/digital_twins_endpoint_operations.py +++ b/azext_iot/sdk/digitaltwins/controlplane/operations/digital_twins_endpoint_operations.py @@ -24,7 +24,7 @@ class DigitalTwinsEndpointOperations(object): :param config: Configuration of service client. :param serializer: An object model serializer. :param deserializer: An object model deserializer. - :ivar api_version: Version of the DigitalTwinsInstance Management API. Constant value: "2020-10-31". + :ivar api_version: Version of the DigitalTwinsInstance Management API. Constant value: "2020-12-01". """ models = models @@ -34,7 +34,7 @@ def __init__(self, client, config, serializer, deserializer): self._client = client self._serialize = serializer self._deserialize = deserializer - self.api_version = "2020-10-31" + self.api_version = "2020-12-01" self.config = config @@ -54,9 +54,9 @@ def list( overrides`. :return: An iterator like instance of DigitalTwinsEndpointResource :rtype: - ~azure.mgmt.digitaltwins.models.DigitalTwinsEndpointResourcePaged[~azure.mgmt.digitaltwins.models.DigitalTwinsEndpointResource] + ~controlplane.models.DigitalTwinsEndpointResourcePaged[~controlplane.models.DigitalTwinsEndpointResource] :raises: - :class:`ErrorResponseException` + :class:`ErrorResponseException` """ def internal_paging(next_link=None, raw=False): @@ -65,7 +65,7 @@ def internal_paging(next_link=None, raw=False): url = self.list.metadata['url'] path_format_arguments = { 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), - 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=64, min_length=1), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1), 'resourceName': self._serialize.url("resource_name", resource_name, 'str', max_length=63, min_length=3, pattern=r'^(?!-)[A-Za-z0-9-]{3,63}(?`. :return: DigitalTwinsEndpointResource or ClientRawResponse if raw=true - :rtype: ~azure.mgmt.digitaltwins.models.DigitalTwinsEndpointResource - or ~msrest.pipeline.ClientRawResponse + :rtype: ~controlplane.models.DigitalTwinsEndpointResource or + ~msrest.pipeline.ClientRawResponse :raises: - :class:`ErrorResponseException` + :class:`ErrorResponseException` """ # Construct URL url = self.get.metadata['url'] path_format_arguments = { 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), - 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=64, min_length=1), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1), 'resourceName': self._serialize.url("resource_name", resource_name, 'str', max_length=63, min_length=3, pattern=r'^(?!-)[A-Za-z0-9-]{3,63}(? if raw==True :rtype: - ~msrestazure.azure_operation.AzureOperationPoller[~azure.mgmt.digitaltwins.models.DigitalTwinsEndpointResource] + ~msrestazure.azure_operation.AzureOperationPoller[~controlplane.models.DigitalTwinsEndpointResource] or - ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~azure.mgmt.digitaltwins.models.DigitalTwinsEndpointResource]] + ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~controlplane.models.DigitalTwinsEndpointResource]] :raises: - :class:`ErrorResponseException` + :class:`ErrorResponseException` """ raw_result = self._create_or_update_initial( resource_group_name=resource_group_name, @@ -290,7 +290,7 @@ def _delete_initial( url = self.delete.metadata['url'] path_format_arguments = { 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), - 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=64, min_length=1), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1), 'resourceName': self._serialize.url("resource_name", resource_name, 'str', max_length=63, min_length=3, pattern=r'^(?!-)[A-Za-z0-9-]{3,63}(? if raw==True :rtype: - ~msrestazure.azure_operation.AzureOperationPoller[~azure.mgmt.digitaltwins.models.DigitalTwinsEndpointResource] + ~msrestazure.azure_operation.AzureOperationPoller[~controlplane.models.DigitalTwinsEndpointResource] or - ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~azure.mgmt.digitaltwins.models.DigitalTwinsEndpointResource]] + ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~controlplane.models.DigitalTwinsEndpointResource]] :raises: - :class:`ErrorResponseException` + :class:`ErrorResponseException` """ raw_result = self._delete_initial( resource_group_name=resource_group_name, diff --git a/azext_iot/sdk/digitaltwins/controlplane/operations/digital_twins_operations.py b/azext_iot/sdk/digitaltwins/controlplane/operations/digital_twins_operations.py index 3a9daa7d6..3dbde2af7 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/operations/digital_twins_operations.py +++ b/azext_iot/sdk/digitaltwins/controlplane/operations/digital_twins_operations.py @@ -24,7 +24,7 @@ class DigitalTwinsOperations(object): :param config: Configuration of service client. :param serializer: An object model serializer. :param deserializer: An object model deserializer. - :ivar api_version: Version of the DigitalTwinsInstance Management API. Constant value: "2020-10-31". + :ivar api_version: Version of the DigitalTwinsInstance Management API. Constant value: "2020-12-01". """ models = models @@ -34,7 +34,7 @@ def __init__(self, client, config, serializer, deserializer): self._client = client self._serialize = serializer self._deserialize = deserializer - self.api_version = "2020-10-31" + self.api_version = "2020-12-01" self.config = config @@ -53,16 +53,16 @@ def get( :param operation_config: :ref:`Operation configuration overrides`. :return: DigitalTwinsDescription or ClientRawResponse if raw=true - :rtype: ~azure.mgmt.digitaltwins.models.DigitalTwinsDescription or + :rtype: ~controlplane.models.DigitalTwinsDescription or ~msrest.pipeline.ClientRawResponse :raises: - :class:`ErrorResponseException` + :class:`ErrorResponseException` """ # Construct URL url = self.get.metadata['url'] path_format_arguments = { 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), - 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=64, min_length=1), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1), 'resourceName': self._serialize.url("resource_name", resource_name, 'str', max_length=63, min_length=3, pattern=r'^(?!-)[A-Za-z0-9-]{3,63}(? if raw==True :rtype: - ~msrestazure.azure_operation.AzureOperationPoller[~azure.mgmt.digitaltwins.models.DigitalTwinsDescription] + ~msrestazure.azure_operation.AzureOperationPoller[~controlplane.models.DigitalTwinsDescription] or - ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~azure.mgmt.digitaltwins.models.DigitalTwinsDescription]] + ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~controlplane.models.DigitalTwinsDescription]] :raises: - :class:`ErrorResponseException` + :class:`ErrorResponseException` """ raw_result = self._create_or_update_initial( resource_group_name=resource_group_name, resource_name=resource_name, - location=location, - tags=tags, + digital_twins_create=digital_twins_create, custom_headers=custom_headers, raw=True, **operation_config @@ -210,35 +207,14 @@ def get_long_running_output(response): return LROPoller(self._client, raw_result, get_long_running_output, polling_method) create_or_update.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DigitalTwins/digitalTwinsInstances/{resourceName}'} - def update( - self, resource_group_name, resource_name, tags=None, custom_headers=None, raw=False, **operation_config): - """Update metadata of DigitalTwinsInstance. - - :param resource_group_name: The name of the resource group that - contains the DigitalTwinsInstance. - :type resource_group_name: str - :param resource_name: The name of the DigitalTwinsInstance. - :type resource_name: str - :param tags: Instance tags - :type tags: dict[str, str] - :param dict custom_headers: headers that will be added to the request - :param bool raw: returns the direct response alongside the - deserialized response - :param operation_config: :ref:`Operation configuration - overrides`. - :return: DigitalTwinsDescription or ClientRawResponse if raw=true - :rtype: ~azure.mgmt.digitaltwins.models.DigitalTwinsDescription or - ~msrest.pipeline.ClientRawResponse - :raises: - :class:`ErrorResponseException` - """ - digital_twins_patch_description = models.DigitalTwinsPatchDescription(tags=tags) + def _update_initial( + self, resource_group_name, resource_name, digital_twins_patch_description, custom_headers=None, raw=False, **operation_config): # Construct URL url = self.update.metadata['url'] path_format_arguments = { 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), - 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=64, min_length=1), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1), 'resourceName': self._serialize.url("resource_name", resource_name, 'str', max_length=63, min_length=3, pattern=r'^(?!-)[A-Za-z0-9-]{3,63}(? if raw==True + :rtype: + ~msrestazure.azure_operation.AzureOperationPoller[~controlplane.models.DigitalTwinsDescription] + or + ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~controlplane.models.DigitalTwinsDescription]] + :raises: + :class:`ErrorResponseException` + """ + raw_result = self._update_initial( + resource_group_name=resource_group_name, + resource_name=resource_name, + digital_twins_patch_description=digital_twins_patch_description, + custom_headers=custom_headers, + raw=True, + **operation_config + ) + + def get_long_running_output(response): + deserialized = self._deserialize('DigitalTwinsDescription', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + + lro_delay = operation_config.get( + 'long_running_operation_timeout', + self.config.long_running_operation_timeout) + if polling is True: polling_method = ARMPolling(lro_delay, **operation_config) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) update.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DigitalTwins/digitalTwinsInstances/{resourceName}'} @@ -287,7 +318,7 @@ def _delete_initial( url = self.delete.metadata['url'] path_format_arguments = { 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), - 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=64, min_length=1), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1), 'resourceName': self._serialize.url("resource_name", resource_name, 'str', max_length=63, min_length=3, pattern=r'^(?!-)[A-Za-z0-9-]{3,63}(? if raw==True :rtype: - ~msrestazure.azure_operation.AzureOperationPoller[~azure.mgmt.digitaltwins.models.DigitalTwinsDescription] + ~msrestazure.azure_operation.AzureOperationPoller[~controlplane.models.DigitalTwinsDescription] or - ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~azure.mgmt.digitaltwins.models.DigitalTwinsDescription]] + ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~controlplane.models.DigitalTwinsDescription]] :raises: - :class:`ErrorResponseException` + :class:`ErrorResponseException` """ raw_result = self._delete_initial( resource_group_name=resource_group_name, @@ -386,9 +417,9 @@ def list( overrides`. :return: An iterator like instance of DigitalTwinsDescription :rtype: - ~azure.mgmt.digitaltwins.models.DigitalTwinsDescriptionPaged[~azure.mgmt.digitaltwins.models.DigitalTwinsDescription] + ~controlplane.models.DigitalTwinsDescriptionPaged[~controlplane.models.DigitalTwinsDescription] :raises: - :class:`ErrorResponseException` + :class:`ErrorResponseException` """ def internal_paging(next_link=None, raw=False): @@ -452,9 +483,9 @@ def list_by_resource_group( overrides`. :return: An iterator like instance of DigitalTwinsDescription :rtype: - ~azure.mgmt.digitaltwins.models.DigitalTwinsDescriptionPaged[~azure.mgmt.digitaltwins.models.DigitalTwinsDescription] + ~controlplane.models.DigitalTwinsDescriptionPaged[~controlplane.models.DigitalTwinsDescription] :raises: - :class:`ErrorResponseException` + :class:`ErrorResponseException` """ def internal_paging(next_link=None, raw=False): @@ -463,7 +494,7 @@ def internal_paging(next_link=None, raw=False): url = self.list_by_resource_group.metadata['url'] path_format_arguments = { 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), - 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=64, min_length=1) + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1) } url = self._client.format_url(url, **path_format_arguments) @@ -519,10 +550,10 @@ def check_name_availability( :param operation_config: :ref:`Operation configuration overrides`. :return: CheckNameResult or ClientRawResponse if raw=true - :rtype: ~azure.mgmt.digitaltwins.models.CheckNameResult or + :rtype: ~controlplane.models.CheckNameResult or ~msrest.pipeline.ClientRawResponse :raises: - :class:`ErrorResponseException` + :class:`ErrorResponseException` """ digital_twins_instance_check_name = models.CheckNameRequest(name=name) diff --git a/azext_iot/sdk/digitaltwins/controlplane/operations/operations.py b/azext_iot/sdk/digitaltwins/controlplane/operations/operations.py index bf1060b5d..2db70e68a 100644 --- a/azext_iot/sdk/digitaltwins/controlplane/operations/operations.py +++ b/azext_iot/sdk/digitaltwins/controlplane/operations/operations.py @@ -22,7 +22,7 @@ class Operations(object): :param config: Configuration of service client. :param serializer: An object model serializer. :param deserializer: An object model deserializer. - :ivar api_version: Version of the DigitalTwinsInstance Management API. Constant value: "2020-10-31". + :ivar api_version: Version of the DigitalTwinsInstance Management API. Constant value: "2020-12-01". """ models = models @@ -32,7 +32,7 @@ def __init__(self, client, config, serializer, deserializer): self._client = client self._serialize = serializer self._deserialize = deserializer - self.api_version = "2020-10-31" + self.api_version = "2020-12-01" self.config = config @@ -47,9 +47,9 @@ def list( overrides`. :return: An iterator like instance of Operation :rtype: - ~azure.mgmt.digitaltwins.models.OperationPaged[~azure.mgmt.digitaltwins.models.Operation] + ~controlplane.models.OperationPaged[~controlplane.models.Operation] :raises: - :class:`ErrorResponseException` + :class:`ErrorResponseException` """ def internal_paging(next_link=None, raw=False): diff --git a/azext_iot/sdk/digitaltwins/controlplane/operations/private_endpoint_connections_operations.py b/azext_iot/sdk/digitaltwins/controlplane/operations/private_endpoint_connections_operations.py new file mode 100644 index 000000000..5b74d372b --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/operations/private_endpoint_connections_operations.py @@ -0,0 +1,364 @@ +# 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 uuid +from msrest.pipeline import ClientRawResponse +from msrest.polling import LROPoller, NoPolling +from msrestazure.polling.arm_polling import ARMPolling + +from .. import models + + +class PrivateEndpointConnectionsOperations(object): + """PrivateEndpointConnectionsOperations operations. + + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + :ivar api_version: Version of the DigitalTwinsInstance Management API. Constant value: "2020-12-01". + """ + + models = models + + def __init__(self, client, config, serializer, deserializer): + + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self.api_version = "2020-12-01" + + self.config = config + + def list( + self, resource_group_name, resource_name, custom_headers=None, raw=False, **operation_config): + """List private endpoint connection properties. + + :param resource_group_name: The name of the resource group that + contains the DigitalTwinsInstance. + :type resource_group_name: str + :param resource_name: The name of the DigitalTwinsInstance. + :type resource_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: PrivateEndpointConnectionsResponse or ClientRawResponse if + raw=true + :rtype: ~controlplane.models.PrivateEndpointConnectionsResponse or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.list.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str', max_length=63, min_length=3, pattern=r'^(?!-)[A-Za-z0-9-]{3,63}(?`. + :return: PrivateEndpointConnection or ClientRawResponse if raw=true + :rtype: ~controlplane.models.PrivateEndpointConnection or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.get.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str', max_length=63, min_length=3, pattern=r'^(?!-)[A-Za-z0-9-]{3,63}(? if raw==True + :rtype: ~msrestazure.azure_operation.AzureOperationPoller[None] or + ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[None]] + :raises: + :class:`ErrorResponseException` + """ + raw_result = self._delete_initial( + resource_group_name=resource_group_name, + resource_name=resource_name, + private_endpoint_connection_name=private_endpoint_connection_name, + custom_headers=custom_headers, + raw=True, + **operation_config + ) + + def get_long_running_output(response): + if raw: + client_raw_response = ClientRawResponse(None, response) + return client_raw_response + + lro_delay = operation_config.get( + 'long_running_operation_timeout', + self.config.long_running_operation_timeout) + if polling is True: polling_method = ARMPolling(lro_delay, **operation_config) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + delete.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DigitalTwins/digitalTwinsInstances/{resourceName}/privateEndpointConnections/{privateEndpointConnectionName}'} + + + def _create_or_update_initial( + self, resource_group_name, resource_name, private_endpoint_connection_name, properties, custom_headers=None, raw=False, **operation_config): + private_endpoint_connection = models.PrivateEndpointConnection(properties=properties) + + # Construct URL + url = self.create_or_update.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str', max_length=63, min_length=3, pattern=r'^(?!-)[A-Za-z0-9-]{3,63}(? if raw==True + :rtype: + ~msrestazure.azure_operation.AzureOperationPoller[~controlplane.models.PrivateEndpointConnection] + or + ~msrestazure.azure_operation.AzureOperationPoller[~msrest.pipeline.ClientRawResponse[~controlplane.models.PrivateEndpointConnection]] + :raises: + :class:`ErrorResponseException` + """ + raw_result = self._create_or_update_initial( + resource_group_name=resource_group_name, + resource_name=resource_name, + private_endpoint_connection_name=private_endpoint_connection_name, + properties=properties, + custom_headers=custom_headers, + raw=True, + **operation_config + ) + + def get_long_running_output(response): + deserialized = self._deserialize('PrivateEndpointConnection', response) + + if raw: + client_raw_response = ClientRawResponse(deserialized, response) + return client_raw_response + + return deserialized + + lro_delay = operation_config.get( + 'long_running_operation_timeout', + self.config.long_running_operation_timeout) + if polling is True: polling_method = ARMPolling(lro_delay, **operation_config) + elif polling is False: polling_method = NoPolling() + else: polling_method = polling + return LROPoller(self._client, raw_result, get_long_running_output, polling_method) + create_or_update.metadata = {'url': '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.DigitalTwins/digitalTwinsInstances/{resourceName}/privateEndpointConnections/{privateEndpointConnectionName}'} diff --git a/azext_iot/sdk/digitaltwins/controlplane/operations/private_link_resources_operations.py b/azext_iot/sdk/digitaltwins/controlplane/operations/private_link_resources_operations.py new file mode 100644 index 000000000..44e7d4c9e --- /dev/null +++ b/azext_iot/sdk/digitaltwins/controlplane/operations/private_link_resources_operations.py @@ -0,0 +1,164 @@ +# 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 uuid +from msrest.pipeline import ClientRawResponse + +from .. import models + + +class PrivateLinkResourcesOperations(object): + """PrivateLinkResourcesOperations operations. + + :param client: Client for service requests. + :param config: Configuration of service client. + :param serializer: An object model serializer. + :param deserializer: An object model deserializer. + :ivar api_version: Version of the DigitalTwinsInstance Management API. Constant value: "2020-12-01". + """ + + models = models + + def __init__(self, client, config, serializer, deserializer): + + self._client = client + self._serialize = serializer + self._deserialize = deserializer + self.api_version = "2020-12-01" + + self.config = config + + def list( + self, resource_group_name, resource_name, custom_headers=None, raw=False, **operation_config): + """List private link resources for given Digital Twin. + + :param resource_group_name: The name of the resource group that + contains the DigitalTwinsInstance. + :type resource_group_name: str + :param resource_name: The name of the DigitalTwinsInstance. + :type resource_name: str + :param dict custom_headers: headers that will be added to the request + :param bool raw: returns the direct response alongside the + deserialized response + :param operation_config: :ref:`Operation configuration + overrides`. + :return: GroupIdInformationResponse or ClientRawResponse if raw=true + :rtype: ~controlplane.models.GroupIdInformationResponse or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.list.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str', max_length=63, min_length=3, pattern=r'^(?!-)[A-Za-z0-9-]{3,63}(?`. + :return: GroupIdInformation or ClientRawResponse if raw=true + :rtype: ~controlplane.models.GroupIdInformation or + ~msrest.pipeline.ClientRawResponse + :raises: + :class:`ErrorResponseException` + """ + # Construct URL + url = self.get.metadata['url'] + path_format_arguments = { + 'subscriptionId': self._serialize.url("self.config.subscription_id", self.config.subscription_id, 'str'), + 'resourceGroupName': self._serialize.url("resource_group_name", resource_group_name, 'str', max_length=90, min_length=1), + 'resourceName': self._serialize.url("resource_name", resource_name, 'str', max_length=63, min_length=3, pattern=r'^(?!-)[A-Za-z0-9-]{3,63}(?= 1: + logger.info("Waiting :{} (sec) for capacity.") + sleep(wait_in_sec) + if self.is_region_available(region=target_region, capacity=capacity): + return + interval = interval - 1 - @dt_resource_group.setter - def dt_resource_group(self, value): - self._rg = value + raise RuntimeError( + "Unavailable region DT capacity. wait(sec): {}, interval: {}, region: {}, capacity: {}".format( + wait_in_sec, interval, target_region, capacity + ) + ) + + def is_region_available(self, region, capacity: int = 1): + region_capacity = self.calculate_region_capacity + return (region_capacity.get(region, 0) + capacity) <= REGION_RESOURCE_LIMIT @property - def dt_resource_group_loc(self): - return self._rg_loc + def calculate_region_capacity(self) -> dict: + instances = self.instances = self.embedded_cli.invoke("dt list").as_json() + capacity_map = {} + for instance in instances: + cap_val = capacity_map.get(instance["location"], 0) + cap_val = cap_val + 1 + capacity_map[instance["location"]] = cap_val + + for region in REGION_LIST: + if region not in capacity_map: + capacity_map[region] = 0 + + return capacity_map + + def get_available_region(self, capacity: int = 1, skip_regions: list = None) -> str: + if not skip_regions: + skip_regions = [] + + region_capacity = self.calculate_region_capacity + + while region_capacity: + region = min(region_capacity, key=region_capacity.get) + if region not in skip_regions: + if region_capacity[region] + capacity <= REGION_RESOURCE_LIMIT: + return region + region_capacity.pop(region, None) + + raise RuntimeError( + "There are no available regions with capacity: {} for provision DT instances in subscription: {}".format( + capacity, self.current_subscription + ) + ) + + def track_instance(self, instance: dict): + self.tracked_instances.append((instance["name"], instance["resourceGroup"])) + + def tearDown(self): + for instance in self.tracked_instances: + self.embedded_cli.invoke( + "dt delete -n {} -g {} -y --no-wait".format(instance[0], instance[1]) + ) + + # Needed because the DT service will indicate provisioning is finished before it actually is. + def wait_for_hostname( + self, instance: dict, wait_in_sec: int = 5, interval: int = 3 + ): + from time import sleep + + while interval >= 1: + logger.info( + "Waiting :{} (sec) for provisioning to complete.".format(wait_in_sec) + ) + sleep(wait_in_sec) + interval = interval - 1 + refereshed_instance = self.embedded_cli.invoke( + "dt show -n {} -g {}".format( + instance["name"], instance["resourceGroup"] + ) + ).as_json() + + if refereshed_instance.get("hostName"): + return refereshed_instance + + return instance diff --git a/azext_iot/tests/digitaltwins/test_dt_model_lifecycle_int.py b/azext_iot/tests/digitaltwins/test_dt_model_lifecycle_int.py index a1faa580f..c4a60b93e 100644 --- a/azext_iot/tests/digitaltwins/test_dt_model_lifecycle_int.py +++ b/azext_iot/tests/digitaltwins/test_dt_model_lifecycle_int.py @@ -13,9 +13,7 @@ process_json_arg, ) from . import DTLiveScenarioTest -from . import ( - generate_resource_id -) +from . import generate_resource_id logger = get_logger(__name__) @@ -26,26 +24,30 @@ def __init__(self, test_case): super(TestDTModelLifecycle, self).__init__(test_case) def test_dt_models(self): + self.wait_for_capacity() + instance_name = generate_resource_id() models_directory = "./models" inline_model = "./models/Floor.json" component_dtmi = "dtmi:com:example:Thermostat;1" room_dtmi = "dtmi:com:example:Room;1" - self.cmd( - "dt create -n {} -g {} -l {}".format( - instance_name, self.dt_resource_group, self.dt_location - ) + self.track_instance( + self.cmd( + "dt create -n {} -g {} -l {}".format( + instance_name, self.rg, self.region + ) + ).get_output_in_json() ) self.cmd( "dt role-assignment create -n {} -g {} --assignee {} --role '{}'".format( - instance_name, self.dt_resource_group, self.current_user, self.role_map["owner"] + instance_name, self.rg, self.current_user, self.role_map["owner"] ) ) # Wait for RBAC to catch-up - sleep(20) + sleep(60) create_models_output = self.cmd( "dt model create -n {} --from-directory '{}'".format( @@ -65,9 +67,7 @@ def test_dt_models(self): assert model["id"] list_models_output = self.cmd( - "dt model list -n {} -g {} --definition".format( - instance_name, self.dt_resource_group - ) + "dt model list -n {} -g {} --definition".format(instance_name, self.rg) ).get_output_in_json() assert len(list_models_output) == len(create_models_output) for model in list_models_output: @@ -76,7 +76,9 @@ def test_dt_models(self): model_dependencies_output = self.cmd( "dt model list -n {} -g {} --dependencies-for '{}'".format( - instance_name, self.dt_resource_group, room_dtmi, + instance_name, + self.rg, + room_dtmi, ) ).get_output_in_json() assert len(model_dependencies_output) == 2 @@ -89,7 +91,7 @@ def test_dt_models(self): model_show_def_output = self.cmd( "dt model show -n {} -g {} --dtmi '{}' --definition".format( - instance_name, self.dt_resource_group, model["id"] + instance_name, self.rg, model["id"] ) ).get_output_in_json() @@ -140,17 +142,12 @@ def test_dt_models(self): == 0 ) - self.cmd( - "dt delete -n {} -g {}".format(instance_name, self.dt_resource_group) - ) - -def assert_create_models_attributes( - result, directory_path=None, models=None, return_metadata=True -): +def assert_create_models_attributes(result, directory_path=None, models=None): if not any([directory_path, models]): raise ValueError("Provide directory_path or models.") + local_test_models = [] if directory_path: local_test_models = _get_models_from_directory(directory_path) diff --git a/azext_iot/tests/digitaltwins/test_dt_privatelinks_lifecycle_int.py b/azext_iot/tests/digitaltwins/test_dt_privatelinks_lifecycle_int.py new file mode 100644 index 000000000..714adfc40 --- /dev/null +++ b/azext_iot/tests/digitaltwins/test_dt_privatelinks_lifecycle_int.py @@ -0,0 +1,176 @@ +# 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.log import get_logger +from . import DTLiveScenarioTest +from . import generate_resource_id, generate_generic_id + +logger = get_logger(__name__) + + +class TestDTPrivateLinksLifecycle(DTLiveScenarioTest): + def __init__(self, test_case): + super(TestDTPrivateLinksLifecycle, self).__init__(test_case) + + def test_dt_privatelinks(self): + self.wait_for_capacity() + + instance_name = generate_resource_id() + group_id = "API" + create_output = self.cmd( + "dt create -n {} -g {} -l {}".format( + instance_name, + self.rg, + self.region, + ) + ).get_output_in_json() + self.track_instance(create_output) + + # Fail test if hostName missing + assert create_output.get( + "hostName" + ), "Service failed to provision DT instance: {}.".format(instance_name) + + list_priv_links = self.cmd( + "dt network private-link list -n {} -g {}".format( + instance_name, + self.rg, + ) + ).get_output_in_json() + assert len(list_priv_links) > 0 + + show_api_priv_link = self.cmd( + "dt network private-link show -n {} -g {} --ln {}".format( + instance_name, self.rg, group_id + ) + ).get_output_in_json() + assert show_api_priv_link["name"] == group_id + assert ( + show_api_priv_link["type"] + == "Microsoft.DigitalTwins/digitalTwinsInstances/privateLinkResources" + ) + + connection_name = generate_generic_id() + endpoint_name = generate_generic_id() + dt_instance_id = create_output["id"] + vnet_name = generate_generic_id() + subnet_name = generate_generic_id() + + # Create VNET + self.cmd( + "network vnet create -n {} -g {} --subnet-name {}".format( + vnet_name, self.rg, subnet_name + ), + checks=self.check("length(newVNet.subnets)", 1), + ) + self.cmd( + "network vnet subnet update -n {} --vnet-name {} -g {} " + "--disable-private-endpoint-network-policies true".format( + subnet_name, vnet_name, self.rg + ), + checks=self.check("privateEndpointNetworkPolicies", "Disabled"), + ) + + create_priv_endpoint_result = self.embedded_cli.invoke( + "network private-endpoint create --connection-name {} -n {} --private-connection-resource-id '{}'" + " --group-id {} -g {} --vnet-name {} --subnet {} --manual-request".format( + connection_name, + endpoint_name, + dt_instance_id, + group_id, + self.rg, + vnet_name, + subnet_name, + ) + ) + + if not create_priv_endpoint_result.success(): + raise RuntimeError( + "Failed to configure private-endpoint for DT instance: {}".format( + instance_name + ) + ) + + list_priv_endpoints = self.cmd( + "dt network private-endpoint connection list -n {} -g {}".format( + instance_name, + self.rg, + ) + ).get_output_in_json() + assert len(list_priv_endpoints) > 0 + + instance_connection_id = list_priv_endpoints[-1]["name"] + + show_priv_endpoint = self.cmd( + "dt network private-endpoint connection show -n {} -g {} --cn {}".format( + instance_name, self.rg, instance_connection_id + ) + ).get_output_in_json() + assert show_priv_endpoint["name"] == instance_connection_id + assert ( + show_priv_endpoint["type"] + == "Microsoft.DigitalTwins/digitalTwinsInstances/privateEndpointConnections" + ) + assert show_priv_endpoint["properties"]["provisioningState"] == "Succeeded" + + # Force manual approval + assert ( + show_priv_endpoint["properties"]["privateLinkServiceConnectionState"]["status"] + == "Pending" + ) + + random_desc_approval = "{} {}".format( + generate_generic_id(), generate_generic_id() + ) + set_connection_output = self.cmd( + "dt network private-endpoint connection set -n {} -g {} --cn {} --status Approved --desc '{}'".format( + instance_name, self.rg, instance_connection_id, random_desc_approval + ) + ).get_output_in_json() + assert ( + set_connection_output["properties"]["privateLinkServiceConnectionState"]["status"] + == "Approved" + ) + assert ( + set_connection_output["properties"]["privateLinkServiceConnectionState"]["description"] + == random_desc_approval + ) + + random_desc_rejected = "{} {}".format( + generate_generic_id(), generate_generic_id() + ) + set_connection_output = self.cmd( + "dt network private-endpoint connection set -n {} -g {} --cn {} --status Rejected --desc '{}'".format( + instance_name, self.rg, instance_connection_id, random_desc_rejected + ) + ).get_output_in_json() + assert ( + set_connection_output["properties"]["privateLinkServiceConnectionState"]["status"] + == "Rejected" + ) + assert ( + set_connection_output["properties"]["privateLinkServiceConnectionState"]["description"] + == random_desc_rejected + ) + + self.cmd( + "dt network private-endpoint connection delete -n {} -g {} --cn {} -y".format( + instance_name, self.rg, instance_connection_id + ) + ) + + list_priv_endpoints = self.cmd( + "dt network private-endpoint connection list -n {} -g {}".format( + instance_name, + self.rg, + ) + ).get_output_in_json() + assert len(list_priv_endpoints) == 0 + + # TODO clean-up optimization + + self.cmd("network private-endpoint delete -n {} -g {} ".format(endpoint_name, self.rg)) + self.cmd("network vnet delete -n {} -g {} ".format(vnet_name, self.rg)) diff --git a/azext_iot/tests/digitaltwins/test_dt_resource_lifecycle_int.py b/azext_iot/tests/digitaltwins/test_dt_resource_lifecycle_int.py index b2f682896..0734fce04 100644 --- a/azext_iot/tests/digitaltwins/test_dt_resource_lifecycle_int.py +++ b/azext_iot/tests/digitaltwins/test_dt_resource_lifecycle_int.py @@ -5,15 +5,18 @@ # -------------------------------------------------------------------------------------------- import pytest +from typing import List +from collections import namedtuple from time import sleep from knack.log import get_logger -from azext_iot.digitaltwins.common import ADTEndpointType +from azext_iot.digitaltwins.common import ADTEndpointAuthType, ADTEndpointType from azext_iot.tests.settings import DynamoSettings from . import DTLiveScenarioTest from . import ( MOCK_RESOURCE_TAGS, MOCK_RESOURCE_TAGS_DICT, MOCK_DEAD_LETTER_SECRET, + MOCK_DEAD_LETTER_ENDPOINT, generate_resource_id, ) @@ -55,23 +58,24 @@ def __init__(self, test_case): super(TestDTResourceLifecycle, self).__init__(test_case) def test_dt_resource(self): - instance_names = [generate_resource_id(), generate_resource_id()] - dt_location_custom = "eastus2euap" + self.wait_for_capacity(capacity=3) + instance_names = [generate_resource_id(), generate_resource_id()] create_output = self.cmd( "dt create -n {} -g {} -l {} --tags {}".format( instance_names[0], - self.dt_resource_group, - self.dt_location, + self.rg, + self.region, MOCK_RESOURCE_TAGS, ) ).get_output_in_json() + self.track_instance(create_output) assert_common_resource_attributes( - create_output, + self.wait_for_hostname(create_output), instance_names[0], - self.dt_resource_group, - self.dt_location, + self.rg, + self.region, MOCK_RESOURCE_TAGS_DICT, ) @@ -79,26 +83,26 @@ def test_dt_resource(self): self.cmd( "dt create -n {} -g {} -l {} --tags {}".format( instance_names[0], - self.dt_resource_group, - dt_location_custom, + self.rg, + self.get_available_region(1, skip_regions=[self.region]), MOCK_RESOURCE_TAGS, ), expect_failure=True, ) # No location specified. Use the resource group location. - create_output = self.cmd( - "dt create -n {} -g {}".format( - instance_names[1], self.dt_resource_group - ) + create_msi_output = self.cmd( + "dt create -n {} -g {} --assign-identity".format(instance_names[1], self.rg) ).get_output_in_json() + self.track_instance(create_msi_output) assert_common_resource_attributes( - create_output, + self.wait_for_hostname(create_msi_output), instance_names[1], - self.dt_resource_group, - self.dt_resource_group_loc, - None, + self.rg, + self.rg_region, + tags=None, + assign_identity=True, ) show_output = self.cmd( @@ -108,21 +112,22 @@ def test_dt_resource(self): assert_common_resource_attributes( show_output, instance_names[0], - self.dt_resource_group, - self.dt_location, + self.rg, + self.region, MOCK_RESOURCE_TAGS_DICT, ) - show_output = self.cmd( - "dt show -n {} -g {}".format(instance_names[1], self.dt_resource_group) + show_msi_output = self.cmd( + "dt show -n {} -g {}".format(instance_names[1], self.rg) ).get_output_in_json() assert_common_resource_attributes( - show_output, + show_msi_output, instance_names[1], - self.dt_resource_group, - self.dt_location, - None, + self.rg, + self.rg_region, + tags=None, + assign_identity=True, ) list_output = self.cmd("dt list").get_output_in_json() @@ -130,27 +135,35 @@ def test_dt_resource(self): assert len(filtered_list) == len(instance_names) list_group_output = self.cmd( - "dt list -g {}".format(self.dt_resource_group) + "dt list -g {}".format(self.rg) ).get_output_in_json() filtered_group_list = filter_dt_list(list_group_output, instance_names) assert len(filtered_group_list) == len(instance_names) # Delete does not currently return output - self.cmd("dt delete -n {}".format(instance_names[0])) + # Delete no blocking self.cmd( - "dt delete -n {} -g {}".format(instance_names[1], self.dt_resource_group) + "dt delete -n {} -g {} -y --no-wait".format(instance_names[1], self.rg) ) + # Delete while blocking + self.cmd("dt delete -n {} -y".format(instance_names[0])) + def test_dt_rbac(self): + self.wait_for_capacity() + rbac_assignee_owner = self.current_user rbac_assignee_reader = self.current_user rbac_instance_name = generate_resource_id() - self.cmd( + rbac_instance = self.cmd( "dt create -n {} -g {} -l {}".format( - rbac_instance_name, self.dt_resource_group, self.dt_location, + rbac_instance_name, + self.rg, + self.region, ) - ) + ).get_output_in_json() + self.track_instance(rbac_instance) assert ( len( @@ -168,7 +181,10 @@ def test_dt_rbac(self): ).get_output_in_json() assert_common_rbac_attributes( - assign_output, rbac_instance_name, "owner", rbac_assignee_owner, + assign_output, + rbac_instance_name, + "owner", + rbac_assignee_owner, ) assign_output = self.cmd( @@ -176,12 +192,15 @@ def test_dt_rbac(self): rbac_instance_name, rbac_assignee_reader, self.role_map["reader"], - self.dt_resource_group, + self.rg, ) ).get_output_in_json() assert_common_rbac_attributes( - assign_output, rbac_instance_name, "reader", rbac_assignee_reader, + assign_output, + rbac_instance_name, + "reader", + rbac_assignee_reader, ) list_assigned_output = self.cmd( @@ -190,19 +209,18 @@ def test_dt_rbac(self): assert len(list_assigned_output) == 2 - # role-assignment delete does not currently return output - # Remove specific role assignment (reader) for assignee + # Role-assignment delete does not currently return output self.cmd( "dt role-assignment delete -n {} --assignee {} --role '{}'".format( - rbac_instance_name, rbac_assignee_owner, self.role_map["reader"], + rbac_instance_name, + rbac_assignee_owner, + self.role_map["reader"], ) ) list_assigned_output = self.cmd( - "dt role-assignment list -n {} -g {}".format( - rbac_instance_name, self.dt_resource_group - ) + "dt role-assignment list -n {} -g {}".format(rbac_instance_name, self.rg) ).get_output_in_json() assert len(list_assigned_output) == 1 @@ -215,26 +233,47 @@ def test_dt_rbac(self): ) list_assigned_output = self.cmd( - "dt role-assignment list -n {} -g {}".format( - rbac_instance_name, self.dt_resource_group - ) + "dt role-assignment list -n {} -g {}".format(rbac_instance_name, self.rg) ).get_output_in_json() assert len(list_assigned_output) == 0 - self.cmd("dt delete -n {}".format(rbac_instance_name)) - @pytest.mark.skipif( not run_endpoint_route_tests, reason="All azext_dt_ep_* env vars are required for endpoint and route tests.", ) def test_dt_endpoints_routes(self): + self.wait_for_capacity() endpoints_instance_name = generate_resource_id() - self.cmd( - "dt create -n {} -g {} -l {}".format( - endpoints_instance_name, self.dt_resource_group, self.dt_location, + target_scope_role = "Contributor" + + sb_topic_resource_id = self.embedded_cli.invoke( + "servicebus topic show --namespace-name {} -n {} -g {}".format( + settings.env.azext_dt_ep_servicebus_namespace, + settings.env.azext_dt_ep_servicebus_topic, + settings.env.azext_dt_ep_rg, ) - ) + ).as_json()["id"] + + eh_resource_id = self.embedded_cli.invoke( + "eventhubs eventhub show --namespace-name {} -n {} -g {}".format( + settings.env.azext_dt_ep_eventhub_namespace, + settings.env.azext_dt_ep_eventhub_topic, + settings.env.azext_dt_ep_rg, + ) + ).as_json()["id"] + + endpoint_instance = self.cmd( + "dt create -n {} -g {} -l {} --assign-identity --scopes {} {} --role {}".format( + endpoints_instance_name, + self.rg, + self.region, + sb_topic_resource_id, + eh_resource_id, + target_scope_role, + ) + ).get_output_in_json() + self.track_instance(endpoint_instance) # Setup RBAC so we can interact with routes self.cmd( @@ -242,12 +281,16 @@ def test_dt_endpoints_routes(self): endpoints_instance_name, self.current_user, self.role_map["owner"], - self.dt_resource_group, + self.rg, ) ) - sleep(60) # Wait for service to catch-up + sleep(30) # Wait for service to catch-up + EndpointTuple = namedtuple( + "endpoint_tuple", ["endpoint_name", "endpoint_type", "auth_type"] + ) + endpoint_tuple_collection: List[EndpointTuple] = [] list_ep_output = self.cmd( "dt endpoint list -n {}".format(endpoints_instance_name) ).get_output_in_json() @@ -257,21 +300,30 @@ def test_dt_endpoints_routes(self): eventgrid_topic = settings.env.azext_dt_ep_eventgrid_topic eventgrid_endpoint = "myeventgridendpoint" - logger.debug("Adding eventgrid endpoint...") + logger.debug("Adding key based eventgrid endpoint...") add_ep_output = self.cmd( "dt endpoint create eventgrid -n {} -g {} --egg {} --egt {} --en {} --dsu {}".format( endpoints_instance_name, - self.dt_resource_group, + self.rg, eventgrid_rg, eventgrid_topic, eventgrid_endpoint, - MOCK_DEAD_LETTER_SECRET + MOCK_DEAD_LETTER_SECRET, ) ).get_output_in_json() assert_common_endpoint_attributes( add_ep_output, eventgrid_endpoint, ADTEndpointType.eventgridtopic, + dead_letter_secret=MOCK_DEAD_LETTER_SECRET, + ) + + endpoint_tuple_collection.append( + EndpointTuple( + eventgrid_endpoint, + ADTEndpointType.eventgridtopic, + ADTEndpointAuthType.keybased, + ) ) servicebus_rg = settings.env.azext_dt_ep_rg @@ -279,9 +331,10 @@ def test_dt_endpoints_routes(self): servicebus_policy = settings.env.azext_dt_ep_servicebus_policy servicebus_topic = settings.env.azext_dt_ep_servicebus_topic servicebus_endpoint = "myservicebusendpoint" + servicebus_endpoint_msi = "{}identity".format(servicebus_endpoint) - logger.debug("Adding servicebus topic endpoint...") - add_ep_output = self.cmd( + logger.debug("Adding key based servicebus topic endpoint...") + add_ep_sb_key_output = self.cmd( "dt endpoint create servicebus -n {} --sbg {} --sbn {} --sbp {} --sbt {} --en {} --dsu {}".format( endpoints_instance_name, servicebus_rg, @@ -289,14 +342,50 @@ def test_dt_endpoints_routes(self): servicebus_policy, servicebus_topic, servicebus_endpoint, - MOCK_DEAD_LETTER_SECRET + MOCK_DEAD_LETTER_SECRET, ) ).get_output_in_json() assert_common_endpoint_attributes( - add_ep_output, + add_ep_sb_key_output, servicebus_endpoint, - ADTEndpointType.servicebus, + endpoint_type=ADTEndpointType.servicebus, + auth_type=ADTEndpointAuthType.keybased, + dead_letter_secret=MOCK_DEAD_LETTER_SECRET, + ) + endpoint_tuple_collection.append( + EndpointTuple( + servicebus_endpoint, + ADTEndpointType.servicebus, + ADTEndpointAuthType.keybased, + ) + ) + + logger.debug("Adding identity based servicebus topic endpoint...") + add_ep_sb_identity_output = self.cmd( + "dt endpoint create servicebus -n {} --sbg {} --sbn {} --sbt {} --en {} --du {} --auth-type IdentityBased".format( + endpoints_instance_name, + servicebus_rg, + servicebus_namespace, + servicebus_topic, + servicebus_endpoint_msi, + MOCK_DEAD_LETTER_ENDPOINT, + ) + ).get_output_in_json() + + assert_common_endpoint_attributes( + add_ep_sb_identity_output, + servicebus_endpoint_msi, + endpoint_type=ADTEndpointType.servicebus, + auth_type=ADTEndpointAuthType.identitybased, + dead_letter_endpoint=MOCK_DEAD_LETTER_ENDPOINT, + ) + endpoint_tuple_collection.append( + EndpointTuple( + servicebus_endpoint_msi, + ADTEndpointType.servicebus, + ADTEndpointAuthType.identitybased, + ) ) eventhub_rg = settings.env.azext_dt_ep_rg @@ -304,8 +393,9 @@ def test_dt_endpoints_routes(self): eventhub_policy = settings.env.azext_dt_ep_eventhub_policy eventhub_topic = settings.env.azext_dt_ep_eventhub_topic eventhub_endpoint = "myeventhubendpoint" + eventhub_endpoint_msi = "{}identity".format(eventhub_endpoint) - logger.debug("Adding eventhub endpoint...") + logger.debug("Adding key based eventhub endpoint...") add_ep_output = self.cmd( "dt endpoint create eventhub -n {} --ehg {} --ehn {} --ehp {} --eh {} --ehs {} --en {} --dsu {}".format( endpoints_instance_name, @@ -315,42 +405,75 @@ def test_dt_endpoints_routes(self): eventhub_topic, self.current_subscription, eventhub_endpoint, - MOCK_DEAD_LETTER_SECRET + MOCK_DEAD_LETTER_SECRET, ) ).get_output_in_json() assert_common_endpoint_attributes( - add_ep_output, eventhub_endpoint, ADTEndpointType.eventhub + add_ep_output, + eventhub_endpoint, + ADTEndpointType.eventhub, + auth_type=ADTEndpointAuthType.keybased, + dead_letter_secret=MOCK_DEAD_LETTER_SECRET, ) - - show_ep_output = self.cmd( - "dt endpoint show -n {} --en {}".format( - endpoints_instance_name, eventhub_endpoint, + endpoint_tuple_collection.append( + EndpointTuple( + eventhub_endpoint, + ADTEndpointType.eventhub, + ADTEndpointAuthType.keybased, ) - ).get_output_in_json() - - assert_common_endpoint_attributes( - show_ep_output, eventhub_endpoint, ADTEndpointType.eventhub ) - show_ep_output = self.cmd( - "dt endpoint show -n {} -g {} --en {}".format( - endpoints_instance_name, self.dt_resource_group, servicebus_endpoint, + logger.debug("Adding identity based eventhub endpoint...") + add_ep_output = self.cmd( + "dt endpoint create eventhub -n {} --ehg {} --ehn {} --eh {} --ehs {} --en {} --du {} " + "--auth-type IdentityBased".format( + endpoints_instance_name, + eventhub_rg, + eventhub_namespace, + eventhub_topic, + self.current_subscription, + eventhub_endpoint_msi, + MOCK_DEAD_LETTER_ENDPOINT, ) ).get_output_in_json() assert_common_endpoint_attributes( - show_ep_output, - servicebus_endpoint, - ADTEndpointType.servicebus, + add_ep_output, + eventhub_endpoint_msi, + endpoint_type=ADTEndpointType.eventhub, + auth_type=ADTEndpointAuthType.identitybased, + dead_letter_endpoint=MOCK_DEAD_LETTER_ENDPOINT, + ) + endpoint_tuple_collection.append( + EndpointTuple( + eventhub_endpoint_msi, + ADTEndpointType.eventhub, + ADTEndpointAuthType.identitybased, + ) ) - list_ep_output = self.cmd( - "dt endpoint list -n {} -g {}".format( - endpoints_instance_name, self.dt_resource_group + for ep in endpoint_tuple_collection: + is_last = ep.endpoint_name == endpoint_tuple_collection[-1].endpoint_name + show_ep_output = self.cmd( + "dt endpoint show -n {} --en {} {}".format( + endpoints_instance_name, + ep.endpoint_name, + "-g {}".format(self.rg) if is_last else "", + ) + ).get_output_in_json() + + assert_common_endpoint_attributes( + show_ep_output, + ep.endpoint_name, + endpoint_type=ep.endpoint_type, + auth_type=ep.auth_type, ) + + list_ep_output = self.cmd( + "dt endpoint list -n {} -g {}".format(endpoints_instance_name, self.rg) ).get_output_in_json() - assert len(list_ep_output) == 3 + assert len(list_ep_output) == 5 endpoint_names = [eventgrid_endpoint, servicebus_endpoint, eventhub_endpoint] filter_values = ["", "false", "type = Microsoft.DigitalTwins.Twin.Create"] @@ -371,7 +494,7 @@ def test_dt_endpoints_routes(self): route_name, endpoint_name, filter_value, - "-g {}".format(self.dt_resource_group) if is_last else "", + "-g {}".format(self.rg) if is_last else "", ) ).get_output_in_json() @@ -383,7 +506,7 @@ def test_dt_endpoints_routes(self): "dt route show -n {} --rn {} {}".format( endpoints_instance_name, route_name, - "-g {}".format(self.dt_resource_group) if is_last else "", + "-g {}".format(self.rg) if is_last else "", ) ).get_output_in_json() @@ -392,9 +515,7 @@ def test_dt_endpoints_routes(self): ) list_routes_output = self.cmd( - "dt route list -n {} -g {}".format( - endpoints_instance_name, self.dt_resource_group - ) + "dt route list -n {} -g {}".format(endpoints_instance_name, self.rg) ).get_output_in_json() assert len(list_routes_output) == 3 @@ -405,49 +526,50 @@ def test_dt_endpoints_routes(self): "dt route delete -n {} --rn {} {}".format( endpoints_instance_name, route_name, - "-g {}".format(self.dt_resource_group) if is_last else "", + "-g {}".format(self.rg) if is_last else "", ) ) list_routes_output = self.cmd( - "dt route list -n {} -g {}".format( - endpoints_instance_name, self.dt_resource_group - ) + "dt route list -n {} -g {}".format(endpoints_instance_name, self.rg) ).get_output_in_json() assert len(list_routes_output) == 0 - # Unfortuntely the service does not yet know how to delete child resouces - # of a dt parent automatically. So we have to explictly delete every endpoint first. - - for endpoint_name in endpoint_names: - logger.debug("Cleaning up {} endpoint...".format(endpoint_name)) - is_last = endpoint_name == endpoint_names[-1] + for ep in endpoint_tuple_collection: + logger.debug("Deleting endpoint {}...".format(ep.endpoint_name)) + is_last = ep.endpoint_name == endpoint_tuple_collection[-1].endpoint_name self.cmd( - "dt endpoint delete -n {} --en {} {}".format( + "dt endpoint delete -y -n {} --en {} {}".format( endpoints_instance_name, - endpoint_name, - "-g {}".format(self.dt_resource_group) if is_last else "", + ep.endpoint_name, + "-g {} --no-wait".format(self.rg) if is_last else "", ) ) list_endpoint_output = self.cmd( - "dt endpoint list -n {} -g {}".format( - endpoints_instance_name, self.dt_resource_group - ) + "dt endpoint list -n {} -g {}".format(endpoints_instance_name, self.rg) ).get_output_in_json() - assert len(list_endpoint_output) == 0 - self.cmd( - "dt delete -n {} -g {}".format( - endpoints_instance_name, self.dt_resource_group + assert ( + len(list_endpoint_output) == 0 + or len( + [ + ep + for ep in list_endpoint_output + if ep["properties"]["provisioningState"].lower() != "deleting" + ] ) + == 0 ) def assert_common_resource_attributes( - instance_output, resource_id, group_id, location, tags + instance_output, resource_id, group_id, location, tags=None, assign_identity=False ): assert instance_output["createdTime"] - assert instance_output["hostName"].startswith(resource_id) + hostname = instance_output.get("hostName") + + assert hostname, "Provisioned instance is missing hostName." + assert hostname.startswith(resource_id) assert instance_output["location"] == location assert instance_output["id"].endswith(resource_id) assert instance_output["lastUpdatedTime"] @@ -455,7 +577,18 @@ def assert_common_resource_attributes( assert instance_output["provisioningState"] == "Succeeded" assert instance_output["resourceGroup"] == group_id assert instance_output["type"] == "Microsoft.DigitalTwins/digitalTwinsInstances" - assert instance_output["tags"] == tags + + if tags: + assert instance_output["tags"] == tags + + if not assign_identity: + assert instance_output["identity"] is None + return + + assert instance_output["identity"]["principalId"] + assert instance_output["identity"]["tenantId"] + # Currently only SystemAssigned identity is supported. + assert instance_output["identity"]["type"] == "SystemAssigned" def assert_common_route_attributes( @@ -467,7 +600,12 @@ def assert_common_route_attributes( def assert_common_endpoint_attributes( - endpoint_output, endpoint_name, endpoint_type, dead_letter_secret=None + endpoint_output, + endpoint_name, + endpoint_type, + dead_letter_secret=None, + dead_letter_endpoint=None, + auth_type=ADTEndpointAuthType.keybased, ): assert endpoint_output["id"].endswith("/{}".format(endpoint_name)) assert ( @@ -475,12 +613,16 @@ def assert_common_endpoint_attributes( == "Microsoft.DigitalTwins/digitalTwinsInstances/endpoints" ) assert endpoint_output["resourceGroup"] - assert endpoint_output["properties"]["provisioningState"] assert endpoint_output["properties"]["createdTime"] + if dead_letter_secret: assert endpoint_output["properties"]["deadLetterSecret"] + if dead_letter_endpoint: + assert endpoint_output["properties"]["deadLetterUri"] + + # Currently DT -> EventGrid is only key based. if endpoint_type == ADTEndpointType.eventgridtopic: assert endpoint_output["properties"]["topicEndpoint"] assert endpoint_output["properties"]["accessKey1"] @@ -488,13 +630,21 @@ def assert_common_endpoint_attributes( assert endpoint_output["properties"]["endpointType"] == "EventGrid" return if endpoint_type == ADTEndpointType.servicebus: - assert endpoint_output["properties"]["primaryConnectionString"] - assert endpoint_output["properties"]["secondaryConnectionString"] + if auth_type == ADTEndpointAuthType.keybased: + assert endpoint_output["properties"]["primaryConnectionString"] + assert endpoint_output["properties"]["secondaryConnectionString"] + if auth_type == ADTEndpointAuthType.identitybased: + assert endpoint_output["properties"]["endpointUri"] + assert endpoint_output["properties"]["entityPath"] assert endpoint_output["properties"]["endpointType"] == "ServiceBus" return if endpoint_type == ADTEndpointType.eventhub: - assert endpoint_output["properties"]["connectionStringPrimaryKey"] - assert endpoint_output["properties"]["connectionStringSecondaryKey"] + if auth_type == ADTEndpointAuthType.keybased: + assert endpoint_output["properties"]["connectionStringPrimaryKey"] + assert endpoint_output["properties"]["connectionStringSecondaryKey"] + if auth_type == ADTEndpointAuthType.identitybased: + assert endpoint_output["properties"]["endpointUri"] + assert endpoint_output["properties"]["entityPath"] assert endpoint_output["properties"]["endpointType"] == "EventHub" return diff --git a/azext_iot/tests/digitaltwins/test_dt_twin_lifecycle_int.py b/azext_iot/tests/digitaltwins/test_dt_twin_lifecycle_int.py index 33b7ab2ea..33280ef19 100644 --- a/azext_iot/tests/digitaltwins/test_dt_twin_lifecycle_int.py +++ b/azext_iot/tests/digitaltwins/test_dt_twin_lifecycle_int.py @@ -22,6 +22,7 @@ def __init__(self, test_case): super(TestDTTwinLifecycle, self).__init__(test_case) def test_dt_twin(self): + self.wait_for_capacity() instance_name = generate_resource_id() models_directory = "./models" floor_dtmi = "dtmi:com:example:Floor;1" @@ -30,15 +31,17 @@ def test_dt_twin(self): room_twin_id = "myroom" thermostat_component_id = "Thermostat" - self.cmd( - "dt create -n {} -g {} -l {}".format( - instance_name, self.dt_resource_group, self.dt_location - ) + self.track_instance( + self.cmd( + "dt create -n {} -g {} -l {}".format( + instance_name, self.rg, self.region + ) + ).get_output_in_json() ) self.cmd( "dt role-assignment create -n {} -g {} --assignee {} --role '{}'".format( - instance_name, self.dt_resource_group, self.current_user, self.role_map["owner"] + instance_name, self.rg, self.current_user, self.role_map["owner"] ) ) # Wait for RBAC to catch-up @@ -93,7 +96,7 @@ def test_dt_twin(self): self.cmd( "dt twin create -n {} -g {} --dtmi {} --twin-id {}".format( instance_name, - self.dt_resource_group, + self.rg, room_dtmi, room_twin_id ), @@ -104,7 +107,7 @@ def test_dt_twin(self): min_room_twin = self.cmd( "dt twin create -n {} -g {} --dtmi {} --twin-id {} --properties '{}'".format( instance_name, - self.dt_resource_group, + self.rg, room_dtmi, room_twin_id, "{emptyThermostatComponentJson}", @@ -122,7 +125,7 @@ def test_dt_twin(self): room_twin = self.cmd( "dt twin create -n {} -g {} --dtmi {} --twin-id {} --properties '{}'".format( instance_name, - self.dt_resource_group, + self.rg, room_dtmi, room_twin_id, "{tempAndThermostatComponentJson}", @@ -142,7 +145,7 @@ def test_dt_twin(self): thermostat_component = self.cmd( "dt twin component show -n {} -g {} --twin-id {} --component {}".format( instance_name, - self.dt_resource_group, + self.rg, room_twin_id, thermostat_component_id, ) @@ -156,7 +159,7 @@ def test_dt_twin(self): self.cmd( "dt twin component update -n {} -g {} --twin-id {} --component {} --json-patch '{}'".format( instance_name, - self.dt_resource_group, + self.rg, room_twin_id, thermostat_component_id, "{thermostatJsonPatch}", @@ -166,7 +169,7 @@ def test_dt_twin(self): thermostat_component = self.cmd( "dt twin component show -n {} -g {} --twin-id {} --component {}".format( instance_name, - self.dt_resource_group, + self.rg, room_twin_id, thermostat_component_id, ) @@ -187,7 +190,7 @@ def test_dt_twin(self): "dt twin show -n {} --twin-id {} {}".format( instance_name, twin_tuple[0], - "-g {}".format(self.dt_resource_group) + "-g {}".format(self.rg) if twins_id_list[-1] == twin_tuple else "", ) @@ -213,7 +216,7 @@ def test_dt_twin(self): twin_query_result = self.cmd( "dt twin query -n {} -g {} -q 'select * from digitaltwins'".format( - instance_name, self.dt_resource_group + instance_name, self.rg ) ).get_output_in_json() assert len(twin_query_result["result"]) == 2 @@ -231,7 +234,7 @@ def test_dt_twin(self): "dt twin relationship create -n {} -g {} --relationship-id {} --relationship {} --twin-id {} " "--target-twin-id {} --properties '{}'".format( instance_name, - self.dt_resource_group, + self.rg, relationship_id, relationship, floor_twin_id, @@ -252,7 +255,7 @@ def test_dt_twin(self): twin_relationship_show_result = self.cmd( "dt twin relationship show -n {} -g {} --twin-id {} --relationship-id {}".format( instance_name, - self.dt_resource_group, + self.rg, floor_twin_id, relationship_id, ) @@ -271,7 +274,7 @@ def test_dt_twin(self): "dt twin relationship update -n {} -g {} --relationship-id {} --twin-id {} " "--json-patch '{}'".format( instance_name, - self.dt_resource_group, + self.rg, relationship_id, floor_twin_id, "{relationshipJsonPatch}", @@ -293,7 +296,7 @@ def test_dt_twin(self): twin_relationship_list_result = self.cmd( "dt twin relationship list -n {} -g {} --twin-id {} --relationship {}".format( instance_name, - self.dt_resource_group, + self.rg, floor_twin_id, relationship, ) @@ -331,7 +334,7 @@ def test_dt_twin(self): twin_relationship_list_result = self.cmd( "dt twin relationship list -n {} -g {} --twin-id {} --kind {}".format( instance_name, - self.dt_resource_group, + self.rg, floor_twin_id, relationship, ) @@ -345,7 +348,7 @@ def test_dt_twin(self): self.cmd( "dt twin telemetry send -n {} -g {} --twin-id {} --telemetry '{}'".format( instance_name, - self.dt_resource_group, + self.rg, room_twin_id, "{telemetryJson}", ) @@ -354,7 +357,7 @@ def test_dt_twin(self): self.cmd( "dt twin telemetry send -n {} -g {} --twin-id {} --component {} --telemetry '{}'".format( instance_name, - self.dt_resource_group, + self.rg, room_twin_id, thermostat_component_id, "{telemetryJson}", @@ -367,7 +370,7 @@ def test_dt_twin(self): "dt twin delete -n {} --twin-id {} {}".format( instance_name, twin_tuple[0], - "-g {}".format(self.dt_resource_group) + "-g {}".format(self.rg) if twins_id_list[-1] == twin_tuple else "", ) @@ -375,16 +378,12 @@ def test_dt_twin(self): sleep(10) # Wait for API to catch up twin_query_result = self.cmd( "dt twin query -n {} -g {} -q 'select * from digitaltwins' --cost".format( - instance_name, self.dt_resource_group + instance_name, self.rg ) ).get_output_in_json() assert len(twin_query_result["result"]) == 0 assert twin_query_result["cost"] - self.cmd( - "dt delete -n {} -g {}".format(instance_name, self.dt_resource_group) - ) - # TODO: Refactor - limited interface def assert_twin_attributes( diff --git a/pytest.ini.example b/pytest.ini.example index b89a4be91..ede7b5ff2 100644 --- a/pytest.ini.example +++ b/pytest.ini.example @@ -19,6 +19,7 @@ env = azext_iot_teststorageuri= azext_iot_teststorageid= azext_iot_central_app_id= + azext_dt_region= azext_dt_ep_eventgrid_topic= azext_dt_ep_servicebus_namespace= azext_dt_ep_servicebus_policy=