Skip to content

Commit

Permalink
Image-Copy: better resource names, allow tags & image name override (#…
Browse files Browse the repository at this point in the history
…151)

* update descriptions and version

* Add checks to verify that the source has a managed disk

* fix procedure to name the temp storage account

* support tags and final image name

* fix lint issue
  • Loading branch information
tamirkamara authored and derekbekoe committed Apr 30, 2018
1 parent 12ff567 commit 10b5fa8
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 19 deletions.
2 changes: 2 additions & 0 deletions src/image-copy/azext_imagecopy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ def load_arguments(self, _):
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')
c.argument('target_name', options_list=['--target-name'],
help='Name of the final image that will be created')


COMMAND_LOADER_CLS = ImageCopyCommandsLoader
9 changes: 7 additions & 2 deletions src/image-copy/azext_imagecopy/cli_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from knack.log import get_logger
logger = get_logger(__name__)

EXTENSION_TAG_STRING = 'created_by=image-copy-extension'


# pylint: disable=inconsistent-return-statements
def run_cli_command(cmd, return_as_json=False):
Expand All @@ -36,7 +38,7 @@ def run_cli_command(cmd, return_as_json=False):
raise


def prepare_cli_command(cmd, output_as_json=True):
def prepare_cli_command(cmd, output_as_json=True, tags=None):
full_cmd = [sys.executable, '-m', 'azure.cli'] + cmd

if output_as_json:
Expand All @@ -46,6 +48,9 @@ def prepare_cli_command(cmd, output_as_json=True):

# tag newly created resources, containers don't have tags
if 'create' in cmd and ('container' not in cmd):
full_cmd += ['--tags', 'created_by=image-copy-extension']
full_cmd += ['--tags', EXTENSION_TAG_STRING]

if tags is not None:
full_cmd += tags.split()

return full_cmd
27 changes: 15 additions & 12 deletions src/image-copy/azext_imagecopy/create_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,20 @@
from knack.log import get_logger
logger = get_logger(__name__)

PROGRESS_LINE_LENGTH = 40
STORAGE_ACCOUNT_NAME_LENGTH = 24


# pylint: disable=too-many-locals
def create_target_image(location, transient_resource_group_name, source_type, source_object_name,
source_os_disk_snapshot_name, source_os_disk_snapshot_url, source_os_type,
target_resource_group_name, azure_pool_frequency):
target_resource_group_name, azure_pool_frequency, tags, target_name):

subscription_id = get_subscription_id()

subscription_hash = hashlib.sha1(
subscription_id.encode("UTF-8")).hexdigest()
unique_subscription_string = subscription_hash[:7]
unique_subscription_string = subscription_hash[:(
STORAGE_ACCOUNT_NAME_LENGTH - len(location))]

# create the target storage account
logger.warn(
Expand Down Expand Up @@ -92,7 +93,7 @@ def create_target_image(location, transient_resource_group_name, source_type, so
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)
location, datetime.datetime.now() - start_datetime)
logger.warn(msg)

# Create the snapshot in the target region from the copied blob
Expand All @@ -112,19 +113,22 @@ def create_target_image(location, transient_resource_group_name, source_type, so

# Create the final image
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
if target_name is None:
target_image_name = source_object_name
if source_type != 'image':
target_image_name += '-image'
target_image_name += '-' + location
else:
target_image_name = target_name

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

'--source', target_snapshot_id], tags=tags)
logger.warn("command: %s", cli_cmd)
run_cli_command(cli_cmd)


Expand All @@ -147,8 +151,7 @@ def wait_for_blob_copy_operation(blob_name, target_container_name, target_storag

if current_progress != prev_progress:
msg = "{0} - Copy progress: {1}%"\
.format(location, str(current_progress))\
.ljust(PROGRESS_LINE_LENGTH) # need to justify since messages overide each other
.format(location, str(current_progress))
logger.warn(msg)

prev_progress = current_progress
Expand Down
15 changes: 13 additions & 2 deletions src/image-copy/azext_imagecopy/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
from azext_imagecopy.cli_utils import run_cli_command, prepare_cli_command
from azext_imagecopy.create_target import create_target_image

from knack.util import CLIError
from knack.log import get_logger
logger = get_logger(__name__)


# pylint: disable=too-many-statements
def imagecopy(source_resource_group_name, source_object_name, target_location,
target_resource_group_name, source_type='image', cleanup='false', parallel_degree=-1):
target_resource_group_name, source_type='image', cleanup='false',
parallel_degree=-1, tags=None, target_name=None):

# get the os disk id from source vm/image
logger.warn("Getting os disk id of the source vm/image")
Expand All @@ -24,6 +26,14 @@ def imagecopy(source_resource_group_name, source_object_name, target_location,

json_cmd_output = run_cli_command(cli_cmd, return_as_json=True)

if 'id' not in json_cmd_output['storageProfile']['osDisk']['managedDisk']:
logger.error(
"It looks like the source resource isn't backed by a managed OS disk. Quitting...")
raise CLIError('Source with no Managed OS disk')

if json_cmd_output['storageProfile']['dataDisks']:
logger.warn("Data disks in the source detected, but are ignored by this extension!")

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",
Expand Down Expand Up @@ -80,7 +90,8 @@ def imagecopy(source_resource_group_name, source_object_name, target_location,
location = location.strip()
tasks.append((location, transient_resource_group_name, source_type,
source_object_name, source_os_disk_snapshot_name, source_os_disk_snapshot_url,
source_os_type, target_resource_group_name, azure_pool_frequency))
source_os_type, target_resource_group_name, azure_pool_frequency,
tags, target_name))

logger.warn("Starting async process for all locations")

Expand Down
6 changes: 3 additions & 3 deletions src/image-copy/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from codecs import open
from setuptools import setup, find_packages

VERSION = "0.0.5"
VERSION = "0.0.6"

CLASSIFIERS = [
'Development Status :: 4 - Beta',
Expand All @@ -29,8 +29,8 @@
setup(
name='image-copy-extension',
version=VERSION,
description='An Azure CLI Extension that copies images from region to region.',
long_description='An Azure CLI Extension that copies images from region to region.',
description='Support for copying managed vm images between regions',
long_description='Support for copying managed vm images between regions',
license='MIT',
author='Tamir Kamara',
author_email='tamir.kamara@microsoft.com',
Expand Down

0 comments on commit 10b5fa8

Please sign in to comment.