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:
Travis Prescott 2018-01-08 12:47:27 -08:00 коммит произвёл Derek Bekoe
Родитель 9e6b525c65
Коммит 76a4dad4a6
13 изменённых файлов: 272 добавлений и 167 удалений

Просмотреть файл

@ -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)

22
src/image-copy/readme.md Normal file
Просмотреть файл

@ -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"
}
}
]
}