From 76a4dad4a604ed042c6becb1b46d1180d5162585 Mon Sep 17 00:00:00 2001 From: Travis Prescott Date: Mon, 8 Jan 2018 12:47:27 -0800 Subject: [PATCH] Merge Knack-based extensions with updated index (#28) * Ignore SDK models/operations and KnackConversion testsdk (#22) * Ignore SDK models/operations and KnackConversion testsdk * Install from KnackConversion branch * Fix new pylint * Ignore knack and other azure.cli modules from pylint import errors (#23) * Ignore _management_client.py from SDK also (#25) * Image-copy: knack conversion (#26) * knack conversion * fixing some lint warnings * fix number displayed in progress reporting * fix more lint issues * fix mroe lint issues * image-copy: fix lint issue * Update IoT Extension index data for new release. (#27) * Update IoT Extension index data for new release. * Tweak dependency format. * minCLiVersion->minCliCoreVersion * Checksum must be lowercase. * Update minCliCoreVersion. * Add new hash. * image copy: add version 0.0.4 back to index (#29) * image copy: add version 0.0.4 back to index * image copy: fix json object --- pylintrc | 2 +- scripts/ci/test_integration.py | 2 +- scripts/ci/test_source.sh | 5 +- scripts/ci/test_static.sh | 9 +- src/image-copy/azext_imagecopy/__init__.py | 80 +++++----- src/image-copy/azext_imagecopy/_help.py | 24 +++ .../azext_imagecopy/azext_metadata.json | 1 + src/image-copy/azext_imagecopy/cli_utils.py | 9 +- .../azext_imagecopy/create_target.py | 149 +++++++++--------- src/image-copy/azext_imagecopy/custom.py | 81 +++++----- src/image-copy/readme.md | 22 +++ src/image-copy/setup.py | 3 +- src/index.json | 62 ++++++-- 13 files changed, 277 insertions(+), 172 deletions(-) create mode 100644 src/image-copy/azext_imagecopy/_help.py create mode 100644 src/image-copy/readme.md diff --git a/pylintrc b/pylintrc index 2c2627215c8..3e0ad5f95e4 100644 --- a/pylintrc +++ b/pylintrc @@ -11,7 +11,7 @@ disable=missing-docstring,locally-disabled,fixme,cyclic-import,too-many-argument [TYPECHECK] # For Azure CLI extensions, we ignore some import errors as they'll be available in the environment of the CLI -ignored-modules=azure,azure.cli +ignored-modules=azure,azure.cli,azure.cli.core,azure.cli.core.commands,knack [FORMAT] max-line-length=120 diff --git a/scripts/ci/test_integration.py b/scripts/ci/test_integration.py index c54e6d052ea..a7aa904b44f 100755 --- a/scripts/ci/test_integration.py +++ b/scripts/ci/test_integration.py @@ -17,8 +17,8 @@ import hashlib import shutil import subprocess -from util import get_repo_root from wheel.install import WHEEL_INFO_RE +from util import get_repo_root INDEX_PATH = os.path.join(get_repo_root(), 'src', 'index.json') SRC_PATH = os.path.join(get_repo_root(), 'src') diff --git a/scripts/ci/test_source.sh b/scripts/ci/test_source.sh index 8b831180a65..4bf414d1342 100755 --- a/scripts/ci/test_source.sh +++ b/scripts/ci/test_source.sh @@ -3,8 +3,9 @@ set -ex # Install CLI & CLI testsdk echo "Installing azure-cli-testsdk and azure-cli..." -# TODO Update the git commit when we need a new version of azure-cli-testsdk -pip install "git+https://github.com/Azure/azure-cli@68460748e47f20cba462686c9fd20d2c720cf98c#egg=azure-cli-testsdk&subdirectory=src/azure-cli-testsdk" -q +# TODO Update the git commit or branch when we need a new version of azure-cli-testsdk +pip install "git+https://github.com/Azure/azure-cli@KnackConversion#egg=azure-cli-testsdk&subdirectory=src/azure-cli-testsdk" -q +pip install knack==0.3.0 -q echo "Installed." diff --git a/scripts/ci/test_static.sh b/scripts/ci/test_static.sh index 8d8b96efc2c..affa3023c92 100755 --- a/scripts/ci/test_static.sh +++ b/scripts/ci/test_static.sh @@ -2,11 +2,16 @@ set -e proc_number=`python -c 'import multiprocessing; print(multiprocessing.cpu_count())'` -pylint ./src/*/azext_*/ --rcfile=./pylintrc -j $proc_number -flake8 --statistics --append-config=./.flake8 ./src/*/azext_*/ +# Run pylint/flake8 on extensions +# - We ignore 'models', 'operations' and files with suffix '_management_client.py' as they typically come from vendored Azure SDKs +pylint ./src/*/azext_*/ --ignore=models,operations,service_bus_management_client --ignore-patterns=[a-zA-Z_]+_management_client.py --rcfile=./pylintrc -j $proc_number +flake8 --statistics --exclude=models,operations,*_management_client.py --append-config=./.flake8 ./src/*/azext_*/ + +# Run pylint/flake8 on CI files pylint ./scripts/ci/*.py --rcfile=./pylintrc flake8 --append-config=./.flake8 ./scripts/ci/*.py +# Other static checks python ./scripts/ci/verify_codeowners.py python ./scripts/ci/verify_license.py diff --git a/src/image-copy/azext_imagecopy/__init__.py b/src/image-copy/azext_imagecopy/__init__.py index 9a145a44ef3..9525a5baaa7 100644 --- a/src/image-copy/azext_imagecopy/__init__.py +++ b/src/image-copy/azext_imagecopy/__init__.py @@ -3,44 +3,42 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- -from azure.cli.core.help_files import helps -from azure.cli.core.sdk.util import ParametersContext - -helps['image copy'] = """ - type: command - short-summary: Copy a managed image (or vm) to other regions - long-summary: > - Allows to copy a managed image (or vm) to other regions. - Keep in mind that it requires the source disk to be available. - examples: - - name: Copy an image to several regions and cleanup at the end. - text: > - az image copy --source-resource-group mySources-rg --source-object-name myImage \\ - --target-location uksouth northeurope --target-resource-group "images-repo-rg" --cleanup - - name: Use an already generalized vm to create images in other regions. - text: > - az image copy --source-resource-group mySources-rg --source-object-name myVm \\ - --source-type vm --target-location uksouth northeurope --target-resource-group "images-repo-rg" -""" - - -def load_params(_): - with ParametersContext('image copy') as c: - c.register('source_resource_group_name', '--source-resource-group', - help='Name of the resource group of the source resource') - c.register('source_object_name', '--source-object-name', - help='The name of the image or vm resource') - c.register('target_location', '--target-location', nargs='+', - help='Space separated location list to create the image in (e.g. westeurope etc.)') - c.register('source_type', '--source-type', default='image', choices=['image', 'vm'], help='image or vm') - c.register('target_resource_group_name', '--target-resource-group', - help='Name of the resource group to create images in') - c.register('parallel_degree', '--parallel-degree', type=int, default=-1, - help='Number of parallel copy operations') - c.register('cleanup', '--cleanup', action='store_true', default=False, - help='Include this switch to delete temporary resources upon completion') - - -def load_commands(): - from azure.cli.core.commands import cli_command - cli_command(__name__, 'image copy', 'azext_imagecopy.custom#imagecopy') +from azure.cli.core import AzCommandsLoader + +import azext_imagecopy._help # pylint: disable=unused-import + + +class ImageCopyCommandsLoader(AzCommandsLoader): + + def __init__(self, cli_ctx=None): + from azure.cli.core.commands import CliCommandType + imagecopy_custom = CliCommandType( + operations_tmpl='azext_imagecopy.custom#{}') + super(ImageCopyCommandsLoader, self).__init__(cli_ctx=cli_ctx, + custom_command_type=imagecopy_custom) + + def load_command_table(self, _): + with self.command_group('image') as g: + g.custom_command('copy', 'imagecopy') + + return self.command_table + + def load_arguments(self, _): + with self.argument_context('image copy') as c: + c.argument('source_resource_group_name', options_list=['--source-resource-group'], + help='Name of the resource group of the source resource') + c.argument('source_object_name', options_list=['--source-object-name'], + help='The name of the image or vm resource') + c.argument('target_location', options_list=['--target-location'], nargs='+', + help='Space separated location list to create the image in (e.g. westeurope etc.)') + c.argument('source_type', options_list=[ + '--source-type'], default='image', choices=['image', 'vm'], help='image or vm') + c.argument('target_resource_group_name', options_list=['--target-resource-group'], + help='Name of the resource group to create images in') + c.argument('parallel_degree', options_list=['--parallel-degree'], type=int, default=-1, + help='Number of parallel copy operations') + c.argument('cleanup', options_list=['--cleanup'], action='store_true', default=False, + help='Include this switch to delete temporary resources upon completion') + + +COMMAND_LOADER_CLS = ImageCopyCommandsLoader diff --git a/src/image-copy/azext_imagecopy/_help.py b/src/image-copy/azext_imagecopy/_help.py new file mode 100644 index 00000000000..50da2c6bdbb --- /dev/null +++ b/src/image-copy/azext_imagecopy/_help.py @@ -0,0 +1,24 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from knack.help_files import helps + + +helps['image copy'] = """ + type: command + short-summary: Copy a managed image (or vm) to other regions + long-summary: > + Allows to copy a managed image (or vm) to other regions. + Keep in mind that it requires the source disk to be available. + examples: + - name: Copy an image to several regions and cleanup at the end. + text: > + az image copy --source-resource-group mySources-rg --source-object-name myImage \\ + --target-location uksouth northeurope --target-resource-group "images-repo-rg" --cleanup + - name: Use an already generalized vm to create images in other regions. + text: > + az image copy --source-resource-group mySources-rg --source-object-name myVm \\ + --source-type vm --target-location uksouth northeurope --target-resource-group "images-repo-rg" +""" diff --git a/src/image-copy/azext_imagecopy/azext_metadata.json b/src/image-copy/azext_imagecopy/azext_metadata.json index 7a73a41bfdf..bbe67260f34 100644 --- a/src/image-copy/azext_imagecopy/azext_metadata.json +++ b/src/image-copy/azext_imagecopy/azext_metadata.json @@ -1,2 +1,3 @@ { + "azext.minCliCoreVersion": "2.0.24" } \ No newline at end of file diff --git a/src/image-copy/azext_imagecopy/cli_utils.py b/src/image-copy/azext_imagecopy/cli_utils.py index d1e33b247c5..fe163c464dc 100644 --- a/src/image-copy/azext_imagecopy/cli_utils.py +++ b/src/image-copy/azext_imagecopy/cli_utils.py @@ -7,18 +7,19 @@ import json from subprocess import check_output, STDOUT, CalledProcessError -from azure.cli.core.util import CLIError -import azure.cli.core.azlogging as azlogging +from knack.util import CLIError -logger = azlogging.get_az_logger(__name__) +from knack.log import get_logger +logger = get_logger(__name__) +# pylint: disable=inconsistent-return-statements def run_cli_command(cmd, return_as_json=False): try: cmd_output = check_output(cmd, stderr=STDOUT, universal_newlines=True) logger.debug('command: %s ended with output: %s', cmd, cmd_output) - if return_as_json is True: + if return_as_json: if cmd_output: json_output = json.loads(cmd_output) return json_output diff --git a/src/image-copy/azext_imagecopy/create_target.py b/src/image-copy/azext_imagecopy/create_target.py index 40539c2c009..71673d34bcc 100644 --- a/src/image-copy/azext_imagecopy/create_target.py +++ b/src/image-copy/azext_imagecopy/create_target.py @@ -7,11 +7,10 @@ import datetime import time from azext_imagecopy.cli_utils import run_cli_command, prepare_cli_command -from azure.cli.core.application import APPLICATION -from azure.cli.core.util import CLIError -import azure.cli.core.azlogging as azlogging +from knack.util import CLIError -logger = azlogging.get_az_logger(__name__) +from knack.log import get_logger +logger = get_logger(__name__) PROGRESS_LINE_LENGTH = 40 @@ -23,27 +22,29 @@ def create_target_image(location, transient_resource_group_name, source_type, so subscription_id = get_subscription_id() - subscription_hash = hashlib.sha1(subscription_id.encode("UTF-8")).hexdigest() + subscription_hash = hashlib.sha1( + subscription_id.encode("UTF-8")).hexdigest() unique_subscription_string = subscription_hash[:7] # create the target storage account - logger.warn("{0} - Creating target storage account (can be slow sometimes)".format(location)) + logger.warn( + "%s - Creating target storage account (can be slow sometimes)", location) target_storage_account_name = location + unique_subscription_string - cmd = prepare_cli_command(['storage', 'account', 'create', - '--name', target_storage_account_name, - '--resource-group', transient_resource_group_name, - '--location', location, - '--sku', 'Standard_LRS']) + cli_cmd = prepare_cli_command(['storage', 'account', 'create', + '--name', target_storage_account_name, + '--resource-group', transient_resource_group_name, + '--location', location, + '--sku', 'Standard_LRS']) - json_output = run_cli_command(cmd, return_as_json=True) + json_output = run_cli_command(cli_cmd, return_as_json=True) target_blob_endpoint = json_output['primaryEndpoints']['blob'] # Setup the target storage account - cmd = prepare_cli_command(['storage', 'account', 'keys', 'list', - '--account-name', target_storage_account_name, - '--resource-group', transient_resource_group_name]) + cli_cmd = prepare_cli_command(['storage', 'account', 'keys', 'list', + '--account-name', target_storage_account_name, + '--resource-group', transient_resource_group_name]) - json_output = run_cli_command(cmd, return_as_json=True) + json_output = run_cli_command(cli_cmd, return_as_json=True) target_storage_account_key = json_output[0]['value'] logger.debug(target_storage_account_key) @@ -51,117 +52,123 @@ def create_target_image(location, transient_resource_group_name, source_type, so expiry_format = "%Y-%m-%dT%H:%MZ" expiry = datetime.datetime.utcnow() + datetime.timedelta(hours=1) - cmd = prepare_cli_command(['storage', 'account', 'generate-sas', - '--account-name', target_storage_account_name, - '--account-key', target_storage_account_key, - '--expiry', expiry.strftime(expiry_format), - '--permissions', 'aclrpuw', '--resource-types', - 'sco', '--services', 'b', '--https-only'], - output_as_json=False) + cli_cmd = prepare_cli_command(['storage', 'account', 'generate-sas', + '--account-name', target_storage_account_name, + '--account-key', target_storage_account_key, + '--expiry', expiry.strftime(expiry_format), + '--permissions', 'aclrpuw', '--resource-types', + 'sco', '--services', 'b', '--https-only'], + output_as_json=False) - sas_token = run_cli_command(cmd) + sas_token = run_cli_command(cli_cmd) sas_token = sas_token.rstrip("\n\r") # STRANGE - logger.debug("sas token: " + sas_token) + logger.debug("sas token: %s", sas_token) # create a container in the target blob storage account - logger.warn("{0} - Creating container in the target storage account".format(location)) + logger.warn( + "%s - Creating container in the target storage account", location) target_container_name = 'snapshots' - cmd = prepare_cli_command(['storage', 'container', 'create', - '--name', target_container_name, - '--account-name', target_storage_account_name]) + cli_cmd = prepare_cli_command(['storage', 'container', 'create', + '--name', target_container_name, + '--account-name', target_storage_account_name]) - run_cli_command(cmd) + run_cli_command(cli_cmd) # Copy the snapshot to the target region using the SAS URL blob_name = source_os_disk_snapshot_name + '.vhd' - logger.warn("{0} - Copying blob to target storage account".format(location)) - cmd = prepare_cli_command(['storage', 'blob', 'copy', 'start', - '--source-uri', source_os_disk_snapshot_url, - '--destination-blob', blob_name, - '--destination-container', target_container_name, - '--account-name', target_storage_account_name, - '--sas-token', sas_token]) + logger.warn( + "%s - Copying blob to target storage account", location) + cli_cmd = prepare_cli_command(['storage', 'blob', 'copy', 'start', + '--source-uri', source_os_disk_snapshot_url, + '--destination-blob', blob_name, + '--destination-container', target_container_name, + '--account-name', target_storage_account_name, + '--sas-token', sas_token]) - run_cli_command(cmd) + run_cli_command(cli_cmd) # Wait for the copy to complete start_datetime = datetime.datetime.now() wait_for_blob_copy_operation(blob_name, target_container_name, target_storage_account_name, azure_pool_frequency, location) - msg = "{0} - Copy time: {1}".format(location, datetime.datetime.now() - start_datetime).ljust(PROGRESS_LINE_LENGTH) + msg = "{0} - Copy time: {1}".format( + location, datetime.datetime.now() - start_datetime).ljust(PROGRESS_LINE_LENGTH) logger.warn(msg) # Create the snapshot in the target region from the copied blob - logger.warn("{0} - Creating snapshot in target region from the copied blob".format(location)) - target_blob_path = target_blob_endpoint + target_container_name + '/' + blob_name + logger.warn( + "%s - Creating snapshot in target region from the copied blob", location) + target_blob_path = target_blob_endpoint + \ + target_container_name + '/' + blob_name target_snapshot_name = source_os_disk_snapshot_name + '-' + location - cmd = prepare_cli_command(['snapshot', 'create', - '--resource-group', transient_resource_group_name, - '--name', target_snapshot_name, - '--location', location, - '--source', target_blob_path]) + cli_cmd = prepare_cli_command(['snapshot', 'create', + '--resource-group', transient_resource_group_name, + '--name', target_snapshot_name, + '--location', location, + '--source', target_blob_path]) - json_output = run_cli_command(cmd, return_as_json=True) + json_output = run_cli_command(cli_cmd, return_as_json=True) target_snapshot_id = json_output['id'] # Create the final image - logger.warn("{0} - Creating final image".format(location)) + logger.warn("%s - Creating final image", location) target_image_name = source_object_name if source_type != 'image': target_image_name += '-image' target_image_name += '-' + location - cmd = prepare_cli_command(['image', 'create', - '--resource-group', target_resource_group_name, - '--name', target_image_name, - '--location', location, - '--source', target_blob_path, - '--os-type', source_os_type, - '--source', target_snapshot_id]) + cli_cmd = prepare_cli_command(['image', 'create', + '--resource-group', target_resource_group_name, + '--name', target_image_name, + '--location', location, + '--source', target_blob_path, + '--os-type', source_os_type, + '--source', target_snapshot_id]) - run_cli_command(cmd) + run_cli_command(cli_cmd) def wait_for_blob_copy_operation(blob_name, target_container_name, target_storage_account_name, azure_pool_frequency, location): - progress_controller = APPLICATION.get_progress_controller() copy_status = "pending" prev_progress = -1 while copy_status == "pending": - cmd = prepare_cli_command(['storage', 'blob', 'show', - '--name', blob_name, - '--container-name', target_container_name, - '--account-name', target_storage_account_name]) + cli_cmd = prepare_cli_command(['storage', 'blob', 'show', + '--name', blob_name, + '--container-name', target_container_name, + '--account-name', target_storage_account_name]) - json_output = run_cli_command(cmd, return_as_json=True) + json_output = run_cli_command(cli_cmd, return_as_json=True) copy_status = json_output["properties"]["copy"]["status"] - copy_progress_1, copy_progress_2 = json_output["properties"]["copy"]["progress"].split("/") - current_progress = round(int(copy_progress_1) / int(copy_progress_2), 1) + copy_progress_1, copy_progress_2 = json_output["properties"]["copy"]["progress"].split( + "/") + current_progress = int( + int(copy_progress_1) / int(copy_progress_2) * 100) if current_progress != prev_progress: - msg = "{0} - copy progress: {1}%"\ + msg = "{0} - Copy progress: {1}%"\ .format(location, str(current_progress))\ .ljust(PROGRESS_LINE_LENGTH) # need to justify since messages overide each other - progress_controller.add(message=msg) + logger.warn(msg) prev_progress = current_progress try: time.sleep(azure_pool_frequency) except KeyboardInterrupt: - progress_controller.stop() return if copy_status == 'success': - progress_controller.stop() + return else: - logger.error("The copy operation didn't succeed. Last status: %s", copy_status) + logger.error( + "The copy operation didn't succeed. Last status: %s", copy_status) raise CLIError('Blob copy failed') def get_subscription_id(): - cmd = prepare_cli_command(['account', 'show']) - json_output = run_cli_command(cmd, return_as_json=True) + cli_cmd = prepare_cli_command(['account', 'show']) + json_output = run_cli_command(cli_cmd, return_as_json=True) subscription_id = json_output['id'] return subscription_id diff --git a/src/image-copy/azext_imagecopy/custom.py b/src/image-copy/azext_imagecopy/custom.py index 01de885922d..7a503982b54 100644 --- a/src/image-copy/azext_imagecopy/custom.py +++ b/src/image-copy/azext_imagecopy/custom.py @@ -7,9 +7,9 @@ from azext_imagecopy.cli_utils import run_cli_command, prepare_cli_command from azext_imagecopy.create_target import create_target_image -import azure.cli.core.azlogging as azlogging -logger = azlogging.get_az_logger(__name__) +from knack.log import get_logger +logger = get_logger(__name__) # pylint: disable=too-many-statements @@ -18,37 +18,39 @@ def imagecopy(source_resource_group_name, source_object_name, target_location, # get the os disk id from source vm/image logger.warn("Getting os disk id of the source vm/image") - cmd = prepare_cli_command([source_type, 'show', - '--name', source_object_name, - '--resource-group', source_resource_group_name]) + cli_cmd = prepare_cli_command([source_type, 'show', + '--name', source_object_name, + '--resource-group', source_resource_group_name]) - json_cmd_output = run_cli_command(cmd, return_as_json=True) + json_cmd_output = run_cli_command(cli_cmd, return_as_json=True) source_os_disk_id = json_cmd_output['storageProfile']['osDisk']['managedDisk']['id'] source_os_type = json_cmd_output['storageProfile']['osDisk']['osType'] - logger.debug("source_os_disk_id: %s. source_os_type: %s", source_os_disk_id, source_os_type) + logger.debug("source_os_disk_id: %s. source_os_type: %s", + source_os_disk_id, source_os_type) # create source snapshots logger.warn("Creating source snapshot") source_os_disk_snapshot_name = source_object_name + '_os_disk_snapshot' - cmd = prepare_cli_command(['snapshot', 'create', - '--name', source_os_disk_snapshot_name, - '--resource-group', source_resource_group_name, - '--source', source_os_disk_id]) + cli_cmd = prepare_cli_command(['snapshot', 'create', + '--name', source_os_disk_snapshot_name, + '--resource-group', source_resource_group_name, + '--source', source_os_disk_id]) - run_cli_command(cmd) + run_cli_command(cli_cmd) # Get SAS URL for the snapshotName logger.warn("Getting sas url for the source snapshot") - cmd = prepare_cli_command(['snapshot', 'grant-access', - '--name', source_os_disk_snapshot_name, - '--resource-group', source_resource_group_name, - '--duration-in-seconds', '3600']) + cli_cmd = prepare_cli_command(['snapshot', 'grant-access', + '--name', source_os_disk_snapshot_name, + '--resource-group', source_resource_group_name, + '--duration-in-seconds', '3600']) - json_output = run_cli_command(cmd, return_as_json=True) + json_output = run_cli_command(cli_cmd, return_as_json=True) source_os_disk_snapshot_url = json_output['accessSas'] - logger.debug("source os disk snapshot url: %s", source_os_disk_snapshot_url) + logger.debug("source os disk snapshot url: %s", + source_os_disk_snapshot_url) # Start processing in the target locations @@ -58,7 +60,8 @@ def imagecopy(source_resource_group_name, source_object_name, target_location, target_locations_count = len(target_location) logger.warn("Target location count: %s", target_locations_count) - create_resource_group(target_resource_group_name, target_location[0].strip()) + create_resource_group(target_resource_group_name, + target_location[0].strip()) if parallel_degree == -1: pool = Pool(target_locations_count) @@ -100,35 +103,37 @@ def imagecopy(source_resource_group_name, source_object_name, target_location, logger.warn('Deleting transient resources') # Delete resource group - cmd = prepare_cli_command(['group', 'delete', '--no-wait', '--yes', - '--name', transient_resource_group_name]) - run_cli_command(cmd) + cli_cmd = prepare_cli_command(['group', 'delete', '--no-wait', '--yes', + '--name', transient_resource_group_name]) + run_cli_command(cli_cmd) # Revoke sas for source snapshot - cmd = prepare_cli_command(['snapshot', 'revoke-access', - '--name', source_os_disk_snapshot_name, - '--resource-group', source_resource_group_name]) - run_cli_command(cmd) + cli_cmd = prepare_cli_command(['snapshot', 'revoke-access', + '--name', source_os_disk_snapshot_name, + '--resource-group', source_resource_group_name]) + run_cli_command(cli_cmd) # Delete source snapshot - cmd = prepare_cli_command(['snapshot', 'delete', - '--name', source_os_disk_snapshot_name, - '--resource-group', source_resource_group_name]) - run_cli_command(cmd) + cli_cmd = prepare_cli_command(['snapshot', 'delete', + '--name', source_os_disk_snapshot_name, + '--resource-group', source_resource_group_name]) + run_cli_command(cli_cmd) def create_resource_group(resource_group_name, location): # check if target resource group exists - cmd = prepare_cli_command(['group', 'exists', - '--name', resource_group_name], output_as_json=False) + cli_cmd = prepare_cli_command(['group', 'exists', + '--name', resource_group_name], output_as_json=False) - cmd_output = run_cli_command(cmd) + cmd_output = run_cli_command(cli_cmd) + + if 'true' in cmd_output: + return - if 'false' in cmd_output: - # create the target resource group - logger.warn("Creating resource group: %s", resource_group_name) - cmd = prepare_cli_command(['group', 'create', + # create the target resource group + logger.warn("Creating resource group: %s", resource_group_name) + cli_cmd = prepare_cli_command(['group', 'create', '--name', resource_group_name, '--location', location]) - run_cli_command(cmd) + run_cli_command(cli_cmd) diff --git a/src/image-copy/readme.md b/src/image-copy/readme.md new file mode 100644 index 00000000000..95de55a4033 --- /dev/null +++ b/src/image-copy/readme.md @@ -0,0 +1,22 @@ +# Azure CLI Image Copy Extension # +This is an extension to azure cli that allows copying virtual machine images between regions with just one command. + +The extension simplifies the process and also enables you to save time by copying to multiple regions in parallel. + +## How to use ## +First, install the extension: +``` +az extension add --name image-copy-extension +``` + +Then, call it as you would any other az command: +``` +az image copy --source-resource-group mySources-rg --source-object-name myImage --target-location uksouth northeurope --target-resource-group "images-repo-rg" --cleanup +``` + +One thing you should keep in mind is that we are relying on the source os disk as the actual source for the copy. So, when you "capture" a new image off a vm in Azure, don't delete the os disk if your intention is to copy it to other regions. + +Other options and examples of using the extensions can be viewed with the help command: +``` +az image copy --help +``` \ No newline at end of file diff --git a/src/image-copy/setup.py b/src/image-copy/setup.py index 1412b91fe0b..986ffc256d9 100644 --- a/src/image-copy/setup.py +++ b/src/image-copy/setup.py @@ -8,7 +8,7 @@ from codecs import open from setuptools import setup, find_packages -VERSION = "0.0.4" +VERSION = "0.0.5" CLASSIFIERS = [ 'Development Status :: 4 - Beta', @@ -36,6 +36,7 @@ author_email='tamir.kamara@microsoft.com', url='https://github.com/Azure/azure-cli-extensions', classifiers=CLASSIFIERS, + package_data={'azext_imagecopy': ['azext_metadata.json']}, packages=find_packages(), install_requires=DEPENDENCIES ) diff --git a/src/index.json b/src/index.json index 5b8525d305b..9b417612acf 100644 --- a/src/index.json +++ b/src/index.json @@ -37,11 +37,11 @@ ], "azure-cli-iot-ext": [ { - "filename": "azure_cli_iot_ext-0.1.1-py2.py3-none-any.whl", - "sha256Digest": "28f5565fa0367da4694223bb8dfacd68be5213e6a49b3a463a10c2379c39da19", - "downloadUrl": "https://files.pythonhosted.org/packages/8e/69/6a10aa6be2ad1a054d874d93bd43c09aaca26a137bd0f8f961f03a249a9f/azure_cli_iot_ext-0.1.1-py2.py3-none-any.whl", + "filename": "azure_cli_iot_ext-0.3.1-py2.py3-none-any.whl", + "sha256Digest": "6932b46e39b5506b460ddc58723d9b2b2223e2a8894d2adda3b0007b4cdef445", + "downloadUrl": "https://github.com/Azure/azure-iot-cli-extension/releases/download/v0.3.1/azure_cli_iot_ext-0.3.1-py2.py3-none-any.whl", "metadata": { - "azext.minCliVersion": "2.0.15+dev", + "azext.minCliCoreVersion": "2.0.24", "classifiers": [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", @@ -80,17 +80,13 @@ "run_requires": [ { "requires": [ - "azure-cli-core", - "azure-cli-iot (==0.1.11)", - "azure-iothub-device-client", - "azure-iothub-service-client", - "azure-mgmt-iothub (==0.2.2)", - "six" + "msrestazure", + "paho-mqtt (==1.3.1)" ] } ], "summary": "Azure IoT CLI Extension", - "version": "0.1.1" + "version": "0.3.1" } } ], @@ -137,6 +133,50 @@ "summary": "An Azure CLI Extension that copies images from region to region.", "version": "0.0.4" } + }, + { + "filename": "image_copy_extension-0.0.5-py2.py3-none-any.whl", + "sha256Digest": "23d3e197bcf9d1017117c3125dd8b86f7b8357bcae831c17a9e0f45f32f9d62b", + "downloadUrl": "https://files.pythonhosted.org/packages/af/d0/7a2d861df60b5d93512ed9341918c614fe6a067f47d47da5063bef7326f6/image_copy_extension-0.0.5-py2.py3-none-any.whl", + "metadata": { + "azext.minCliCoreVersion": "2.0.24", + "classifiers": [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "License :: OSI Approved :: MIT License" + ], + "extensions": { + "python.details": { + "contacts": [ + { + "email": "tamir.kamara@microsoft.com", + "name": "Tamir Kamara", + "role": "author" + } + ], + "document_names": { + "description": "DESCRIPTION.rst" + }, + "project_urls": { + "Home": "https://github.com/Azure/azure-cli-extensions" + } + } + }, + "generator": "bdist_wheel (0.29.0)", + "license": "MIT", + "metadata_version": "2.0", + "name": "image-copy-extension", + "summary": "An Azure CLI Extension that copies images from region to region.", + "version": "0.0.5" + } } ] }