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
This commit is contained in:
Родитель
9e6b525c65
Коммит
76a4dad4a6
2
pylintrc
2
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
|
||||
|
|
|
@ -17,8 +17,8 @@ import zipfile
|
|||
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')
|
||||
|
|
|
@ -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."
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
from azure.cli.core import AzCommandsLoader
|
||||
|
||||
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"
|
||||
"""
|
||||
import azext_imagecopy._help # pylint: disable=unused-import
|
||||
|
||||
|
||||
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')
|
||||
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')
|
||||
|
||||
|
||||
def load_commands():
|
||||
from azure.cli.core.commands import cli_command
|
||||
cli_command(__name__, 'image copy', 'azext_imagecopy.custom#imagecopy')
|
||||
COMMAND_LOADER_CLS = ImageCopyCommandsLoader
|
||||
|
|
|
@ -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"
|
||||
"""
|
|
@ -1,2 +1,3 @@
|
|||
{
|
||||
"azext.minCliCoreVersion": "2.0.24"
|
||||
}
|
|
@ -7,18 +7,19 @@ import sys
|
|||
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
|
||||
|
|
|
@ -7,11 +7,10 @@ import hashlib
|
|||
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
|
||||
|
|
|
@ -7,9 +7,9 @@ from multiprocessing import Pool
|
|||
|
||||
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 'false' in cmd_output:
|
||||
# create the target resource group
|
||||
logger.warn("Creating resource group: %s", resource_group_name)
|
||||
cmd = prepare_cli_command(['group', 'create',
|
||||
if 'true' in cmd_output:
|
||||
return
|
||||
|
||||
# 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)
|
||||
|
|
|
@ -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
|
||||
```
|
|
@ -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 @@ setup(
|
|||
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
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче