From 52a6cbe5aaf059de1208273f800d59154bcad71e Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Fri, 6 Nov 2020 20:53:56 -0800 Subject: [PATCH 1/6] Partial work on create command. --- src/quantum/azext_quantum/commands.py | 1 + .../azext_quantum/operations/workspace.py | 27 ++++++++++++++++ .../operations/workspaces_operations.py | 31 ++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/quantum/azext_quantum/commands.py b/src/quantum/azext_quantum/commands.py index de94c55da57..5cae2c14e44 100644 --- a/src/quantum/azext_quantum/commands.py +++ b/src/quantum/azext_quantum/commands.py @@ -82,6 +82,7 @@ def load_command_table(self, _): target_ops = CliCommandType(operations_tmpl='azext_quantum.operations.target#{}') with self.command_group('quantum workspace', workspace_ops) as w: + w.command('create', 'create', validator=validate_workspace_info) w.command('delete', 'delete', validator=validate_workspace_info) w.command('list', 'list') w.command('show', 'show', validator=validate_workspace_info) diff --git a/src/quantum/azext_quantum/operations/workspace.py b/src/quantum/azext_quantum/operations/workspace.py index c6a2e5adf3f..7d1839ea500 100644 --- a/src/quantum/azext_quantum/operations/workspace.py +++ b/src/quantum/azext_quantum/operations/workspace.py @@ -8,6 +8,8 @@ from knack.util import CLIError from .._client_factory import cf_workspaces +from ..vendored_sdks.azure_mgmt_quantum.models import QuantumWorkspace +from ..vendored_sdks.azure_mgmt_quantum.models import Provider class WorkspaceInfo(object): @@ -43,6 +45,31 @@ def save(self, cmd): cmd.cli_ctx.config.set_value('quantum', 'group', self.resource_group) cmd.cli_ctx.config.set_value('quantum', 'workspace', self.name) +def create(cmd, resource_group_name=None, workspace_name=None): + """ + Creates a new Azure Quantum workspace. + """ + print("Create attempted.") + client = cf_workspaces(cmd.cli_ctx) + info = WorkspaceInfo(cmd, resource_group_name, workspace_name) + if (not info.resource_group) or (not info.name): + raise CLIError("Please run 'az quantum workspace set' first to select a default Quantum Workspace.") + # Default provider + prov = Provider() + prov.provider_id = "Microsoft" + prov.provider_sku = "Basic" + # Create a Quantum Workspace object and set the required properties + qw = QuantumWorkspace() + qw.location = "West US" + qw.providers = [prov] + qw.storage_account = "/subscriptions/916dfd6d-030c-4bd9-b579-7bb6d1926e97/resourceGroups/aqua-testing-westus2/providers/Microsoft.Storage/storageAccounts/ricardoeaqd01" + print(qw) + print("Create attempted. 2") + client.create_and_update(info.resource_group, info.name, qw, raw=True, polling=False) + return qw + + + def delete(cmd, resource_group_name=None, workspace_name=None): """ Deletes the given (or current) Azure Quantum workspace. diff --git a/src/quantum/azext_quantum/vendored_sdks/azure_mgmt_quantum/operations/workspaces_operations.py b/src/quantum/azext_quantum/vendored_sdks/azure_mgmt_quantum/operations/workspaces_operations.py index 5969c8cc7b2..aa96f2b8a41 100644 --- a/src/quantum/azext_quantum/vendored_sdks/azure_mgmt_quantum/operations/workspaces_operations.py +++ b/src/quantum/azext_quantum/vendored_sdks/azure_mgmt_quantum/operations/workspaces_operations.py @@ -126,12 +126,35 @@ def _create_and_update_initial( if self.config.accept_language is not None: header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') + print("A") + # Construct body body_content = self._serialize.body(quantum_workspace, 'QuantumWorkspace') + print("B") + # Construct and send request request = self._client.put(url, query_parameters, header_parameters, body_content) - response = self._client.send(request, stream=False, **operation_config) + + print("Inner request:") + print(request) + + print(url) + print(query_parameters) + print(header_parameters) + print(body_content) + + print("C") + + try: + response = self._client.send(request, stream=False, **operation_config) + except Exception as e: + print(e) + print("Exception end") + return + + print("Inner response:") + print(response) if response.status_code not in [200, 201]: raise models.ErrorResponseException(self._deserialize, response) @@ -182,6 +205,10 @@ def create_and_update( **operation_config ) + print("Raw: ") + print(raw_result) + print("Deserialized: ") + def get_long_running_output(response): deserialized = self._deserialize('QuantumWorkspace', response) @@ -191,6 +218,8 @@ def get_long_running_output(response): return deserialized + print(get_long_running_output(raw_result)) + lro_delay = operation_config.get( 'long_running_operation_timeout', self.config.long_running_operation_timeout) From a0f7f45265e61d7092f3b9beda25a20e59dac5a2 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sat, 7 Nov 2020 18:18:15 -0800 Subject: [PATCH 2/6] Restoring unmodified generated file --- .../operations/workspaces_operations.py | 31 +------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/src/quantum/azext_quantum/vendored_sdks/azure_mgmt_quantum/operations/workspaces_operations.py b/src/quantum/azext_quantum/vendored_sdks/azure_mgmt_quantum/operations/workspaces_operations.py index aa96f2b8a41..5969c8cc7b2 100644 --- a/src/quantum/azext_quantum/vendored_sdks/azure_mgmt_quantum/operations/workspaces_operations.py +++ b/src/quantum/azext_quantum/vendored_sdks/azure_mgmt_quantum/operations/workspaces_operations.py @@ -126,35 +126,12 @@ def _create_and_update_initial( if self.config.accept_language is not None: header_parameters['accept-language'] = self._serialize.header("self.config.accept_language", self.config.accept_language, 'str') - print("A") - # Construct body body_content = self._serialize.body(quantum_workspace, 'QuantumWorkspace') - print("B") - # Construct and send request request = self._client.put(url, query_parameters, header_parameters, body_content) - - print("Inner request:") - print(request) - - print(url) - print(query_parameters) - print(header_parameters) - print(body_content) - - print("C") - - try: - response = self._client.send(request, stream=False, **operation_config) - except Exception as e: - print(e) - print("Exception end") - return - - print("Inner response:") - print(response) + response = self._client.send(request, stream=False, **operation_config) if response.status_code not in [200, 201]: raise models.ErrorResponseException(self._deserialize, response) @@ -205,10 +182,6 @@ def create_and_update( **operation_config ) - print("Raw: ") - print(raw_result) - print("Deserialized: ") - def get_long_running_output(response): deserialized = self._deserialize('QuantumWorkspace', response) @@ -218,8 +191,6 @@ def get_long_running_output(response): return deserialized - print(get_long_running_output(raw_result)) - lro_delay = operation_config.get( 'long_running_operation_timeout', self.config.long_running_operation_timeout) From fcbaad6a73c898c992d35e278f28892520b361f0 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Mon, 9 Nov 2020 15:54:26 -0800 Subject: [PATCH 3/6] Completing implementation of az quantum workspace create --- src/quantum/azext_quantum/_help.py | 3 ++ src/quantum/azext_quantum/_params.py | 2 + src/quantum/azext_quantum/commands.py | 2 +- .../azext_quantum/operations/workspace.py | 43 +++++++++++-------- .../tests/latest/test_quantum_workspace.py | 15 ++++--- .../azext_quantum/tests/latest/utils.py | 1 + 6 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/quantum/azext_quantum/_help.py b/src/quantum/azext_quantum/_help.py index a8e07a3ad39..d2f01030ffa 100644 --- a/src/quantum/azext_quantum/_help.py +++ b/src/quantum/azext_quantum/_help.py @@ -54,6 +54,9 @@ - name: Get the list of Azure Quantum workspaces available text: |- az quantum workspace list + - name: Create a new Azure Quantum workspace + text: |- + az quantum workspace create -g MyResourceGroup -w MyWorkspace -l MyLocation -sa MyStorageAccountName - name: Delete an Azure Quantum workspace that is no longer being used text: |- az quantum workspace delete -g MyResourceGroup -w MyWorkspace diff --git a/src/quantum/azext_quantum/_params.py b/src/quantum/azext_quantum/_params.py index 085a69f637a..349005032bc 100644 --- a/src/quantum/azext_quantum/_params.py +++ b/src/quantum/azext_quantum/_params.py @@ -9,6 +9,7 @@ def load_arguments(self, _): workspace_name_type = CLIArgumentType(options_list=['--workspace-name', '-w'], help='Name of the Quantum Workspace. You can configure the default workspace using `az quantum workspace set`.', id_part=None, required=False) + storage_account_name_type = CLIArgumentType(options_list=['--storage_account', '-sa'], help='Name of the storage account to be used by a quantum workspace.') program_args_type = CLIArgumentType(nargs='*', help='List of arguments expected by the Q# operation specified as --name=value after `--`.') target_id_type = CLIArgumentType(options_list=['--target-id', '-t'], help='Target id.') project_type = CLIArgumentType(help='The location of the Q# project to submit. Defaults to current folder.') @@ -19,6 +20,7 @@ def load_arguments(self, _): with self.argument_context('quantum workspace') as c: c.argument('workspace_name', workspace_name_type) + c.argument('storage_account', storage_account_name_type) with self.argument_context('quantum target') as c: c.argument('workspace_name', workspace_name_type) diff --git a/src/quantum/azext_quantum/commands.py b/src/quantum/azext_quantum/commands.py index 5cae2c14e44..a5481ca273d 100644 --- a/src/quantum/azext_quantum/commands.py +++ b/src/quantum/azext_quantum/commands.py @@ -82,7 +82,7 @@ def load_command_table(self, _): target_ops = CliCommandType(operations_tmpl='azext_quantum.operations.target#{}') with self.command_group('quantum workspace', workspace_ops) as w: - w.command('create', 'create', validator=validate_workspace_info) + w.command('create', 'create') w.command('delete', 'delete', validator=validate_workspace_info) w.command('list', 'list') w.command('show', 'show', validator=validate_workspace_info) diff --git a/src/quantum/azext_quantum/operations/workspace.py b/src/quantum/azext_quantum/operations/workspace.py index 7d1839ea500..504c5f29c5e 100644 --- a/src/quantum/azext_quantum/operations/workspace.py +++ b/src/quantum/azext_quantum/operations/workspace.py @@ -9,6 +9,7 @@ from .._client_factory import cf_workspaces from ..vendored_sdks.azure_mgmt_quantum.models import QuantumWorkspace +from ..vendored_sdks.azure_mgmt_quantum.models import QuantumWorkspaceIdentity from ..vendored_sdks.azure_mgmt_quantum.models import Provider @@ -45,30 +46,38 @@ def save(self, cmd): cmd.cli_ctx.config.set_value('quantum', 'group', self.resource_group) cmd.cli_ctx.config.set_value('quantum', 'workspace', self.name) -def create(cmd, resource_group_name=None, workspace_name=None): - """ - Creates a new Azure Quantum workspace. - """ - print("Create attempted.") - client = cf_workspaces(cmd.cli_ctx) - info = WorkspaceInfo(cmd, resource_group_name, workspace_name) - if (not info.resource_group) or (not info.name): - raise CLIError("Please run 'az quantum workspace set' first to select a default Quantum Workspace.") - # Default provider +def get_basic_quantum_workspace(location, info, storage_account): + qw = QuantumWorkspace() + # Use a default provider + # Replace this with user specified providers as part of task: + # https://ms-quantum.visualstudio.com/Quantum%20Program/_workitems/edit/16184 prov = Provider() prov.provider_id = "Microsoft" prov.provider_sku = "Basic" - # Create a Quantum Workspace object and set the required properties - qw = QuantumWorkspace() - qw.location = "West US" qw.providers = [prov] - qw.storage_account = "/subscriptions/916dfd6d-030c-4bd9-b579-7bb6d1926e97/resourceGroups/aqua-testing-westus2/providers/Microsoft.Storage/storageAccounts/ricardoeaqd01" - print(qw) - print("Create attempted. 2") - client.create_and_update(info.resource_group, info.name, qw, raw=True, polling=False) + # Allow the system to assign the workspace identity + qw.identity = QuantumWorkspaceIdentity() + qw.identity.type = "SystemAssigned" + qw.location=location + qw.storage_account = "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Storage/storageAccounts/{2}".format(info.subscription, info.resource_group, storage_account) return qw +def create(cmd, resource_group_name=None, workspace_name=None, location=None, storage_account=None): + """ + Creates a new Azure Quantum workspace. + """ + client = cf_workspaces(cmd.cli_ctx) + if (not workspace_name): + raise CLIError("An explicit workspace name is required for this command.") + if (not storage_account): + raise CLIError("A quantum workspace requires a valid storage account.") + info = WorkspaceInfo(cmd, resource_group_name, workspace_name) + if (not info.resource_group): + raise CLIError("Please run 'az quantum workspace set' first to select a default Quantum Workspace.") + quantum_workspace = get_basic_quantum_workspace(location, info, storage_account) + return client.create_and_update(info.resource_group, info.name, quantum_workspace, polling=False) + def delete(cmd, resource_group_name=None, workspace_name=None): """ diff --git a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py index 862ebc4fb9a..220a26c015f 100644 --- a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py +++ b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py @@ -46,9 +46,14 @@ def test_workspace(self): # clear self.cmd(f'az quantum workspace clear') + # create + self.cmd(f'az quantum workspace create -g {TEST_RG} -w {TEST_WORKSPACE_CREATE_DELETE} -l {TEST_WORKSPACE_LOCATION} -sa {TEST_WORKSPACE_SA} -o json'), checks=[ + self.check("name", TEST_WORKSPACE_CREATE_DELETE), + self.check("provisioningState", "Succeeded") + ]) + # delete - ## TODO: This test is disabled until the 'create' command is added. - # self.cmd(f'az quantum workspace delete -g {TEST_RG} -w {TEST_WORKSPACE_CREATE_DELETE} -o json'), checks=[ - # self.check("name", TEST_WORKSPACE_CREATE_DELETE), - # self.check("provisioningState", ) - #]) + self.cmd(f'az quantum workspace delete -g {TEST_RG} -w {TEST_WORKSPACE_CREATE_DELETE} -o json'), checks=[ + self.check("name", TEST_WORKSPACE_CREATE_DELETE), + self.check("provisioningState", "Deleting") + ]) diff --git a/src/quantum/azext_quantum/tests/latest/utils.py b/src/quantum/azext_quantum/tests/latest/utils.py index a22c23f78b9..30cd7774a3d 100644 --- a/src/quantum/azext_quantum/tests/latest/utils.py +++ b/src/quantum/azext_quantum/tests/latest/utils.py @@ -7,6 +7,7 @@ TEST_RG = 'aqua-provider-validator' TEST_WORKSPACE = 'validator-workspace-westus' TEST_WORKSPACE_CREATE_DELETE = 'validator-workspace-crdl-westus' +TEST_WORKSPACE_SA = 'validatorsa' def is_private_preview_subscription(scenario): From 773c00811067cd21c05fd518341d1c28512d85a6 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Mon, 9 Nov 2020 16:01:38 -0800 Subject: [PATCH 4/6] Fixing name of storage account used for testing. --- src/quantum/azext_quantum/tests/latest/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/quantum/azext_quantum/tests/latest/utils.py b/src/quantum/azext_quantum/tests/latest/utils.py index 30cd7774a3d..829f35e9176 100644 --- a/src/quantum/azext_quantum/tests/latest/utils.py +++ b/src/quantum/azext_quantum/tests/latest/utils.py @@ -7,7 +7,8 @@ TEST_RG = 'aqua-provider-validator' TEST_WORKSPACE = 'validator-workspace-westus' TEST_WORKSPACE_CREATE_DELETE = 'validator-workspace-crdl-westus' -TEST_WORKSPACE_SA = 'validatorsa' +TEST_WORKSPACE_SA = 'aqvalidatorstorage' +TEST_WORKSPACE_LOCATION = 'westus' def is_private_preview_subscription(scenario): From 609f52eeef9450d4f88e4b1e542ed204c61a9a91 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Mon, 9 Nov 2020 16:04:19 -0800 Subject: [PATCH 5/6] Fix accidental indentation --- .../azext_quantum/tests/latest/test_quantum_workspace.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py index 220a26c015f..b011cf93358 100644 --- a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py +++ b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py @@ -47,13 +47,13 @@ def test_workspace(self): self.cmd(f'az quantum workspace clear') # create - self.cmd(f'az quantum workspace create -g {TEST_RG} -w {TEST_WORKSPACE_CREATE_DELETE} -l {TEST_WORKSPACE_LOCATION} -sa {TEST_WORKSPACE_SA} -o json'), checks=[ + self.cmd(f'az quantum workspace create -g {TEST_RG} -w {TEST_WORKSPACE_CREATE_DELETE} -l {TEST_WORKSPACE_LOCATION} -sa {TEST_WORKSPACE_SA} -o json'), checks=[ self.check("name", TEST_WORKSPACE_CREATE_DELETE), self.check("provisioningState", "Succeeded") ]) # delete - self.cmd(f'az quantum workspace delete -g {TEST_RG} -w {TEST_WORKSPACE_CREATE_DELETE} -o json'), checks=[ + self.cmd(f'az quantum workspace delete -g {TEST_RG} -w {TEST_WORKSPACE_CREATE_DELETE} -o json'), checks=[ self.check("name", TEST_WORKSPACE_CREATE_DELETE), self.check("provisioningState", "Deleting") ]) From c48e792f271f641ace37533110390e06c41260e4 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Tue, 10 Nov 2020 21:55:20 -0800 Subject: [PATCH 6/6] Code review feedback. --- src/quantum/azext_quantum/operations/workspace.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quantum/azext_quantum/operations/workspace.py b/src/quantum/azext_quantum/operations/workspace.py index 504c5f29c5e..c00561f34b3 100644 --- a/src/quantum/azext_quantum/operations/workspace.py +++ b/src/quantum/azext_quantum/operations/workspace.py @@ -58,8 +58,8 @@ def get_basic_quantum_workspace(location, info, storage_account): # Allow the system to assign the workspace identity qw.identity = QuantumWorkspaceIdentity() qw.identity.type = "SystemAssigned" - qw.location=location - qw.storage_account = "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Storage/storageAccounts/{2}".format(info.subscription, info.resource_group, storage_account) + qw.location = location + qw.storage_account = f"/subscriptions/{info.subscription}/resourceGroups/{info.resource_group}/providers/Microsoft.Storage/storageAccounts/{storage_account}" return qw