diff --git a/src/azure-cli/HISTORY.rst b/src/azure-cli/HISTORY.rst index e4ec67e2bd6..afc837972da 100644 --- a/src/azure-cli/HISTORY.rst +++ b/src/azure-cli/HISTORY.rst @@ -34,6 +34,7 @@ Release History * vm create: Add warning when specifying accelerated networking and an existing NIC together. * vm create: Add --vmss to specify an existing virtual machine scale set that the virtual machine should be assigned to. +* vm/vmss create: Add a local copy of image alias file so that it can be accessed in a restricted network environment. * vmss create: Add --orchestration-mode to specify how virtual machines are managed by the scale set. * vm/vmss update: Add --ultra-ssd-enabled to allow updating ultra SSD setting. * [BREAKING CHANGE] vm extension set: Fix bug where users could not set an extension on a VM with --ids. diff --git a/src/azure-cli/azure/cli/command_modules/vm/_actions.py b/src/azure-cli/azure/cli/command_modules/vm/_actions.py index 38ce96d9877..32ef889d424 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/_actions.py +++ b/src/azure-cli/azure/cli/command_modules/vm/_actions.py @@ -6,12 +6,15 @@ import json from knack.util import CLIError +from knack.log import get_logger from azure.cli.core.commands.parameters import get_one_of_subscription_locations from azure.cli.core.commands.arm import resource_exists from ._client_factory import _compute_client_factory +logger = get_logger(__name__) + def _resource_not_exists(cli_ctx, resource_type): def _handle_resource_not_exists(namespace): @@ -79,10 +82,19 @@ def load_images_from_aliases_doc(cli_ctx, publisher=None, offer=None, sku=None): raise CLIError("'endpoint_vm_image_alias_doc' isn't configured. Please invoke 'az cloud update' to configure " "it or use '--all' to retrieve images from server") # under hack mode(say through proxies with unsigned cert), opt out the cert verification - response = requests.get(target_url, verify=(not should_disable_connection_verify())) - if response.status_code != 200: - raise CLIError("Failed to retrieve image alias doc '{}'. Error: '{}'".format(target_url, response)) - dic = json.loads(response.content.decode()) + from azure.cli.command_modules.vm._alias import alias_json + try: + response = requests.get(target_url, verify=(not should_disable_connection_verify())) + if response.status_code == 200: + dic = json.loads(response.content.decode()) + else: + logger.warning("Failed to retrieve image alias doc '%s'. Error: '%s'. Use local copy instead.", + target_url, response) + dic = json.loads(alias_json) + except requests.exceptions.ConnectionError: + logger.warning("Failed to retrieve image alias doc '%s'. Error: 'ConnectionError'. Use local copy instead.", + target_url) + dic = json.loads(alias_json) try: all_images = [] result = (dic['outputs']['aliases']['value']) @@ -101,7 +113,7 @@ def load_images_from_aliases_doc(cli_ctx, publisher=None, offer=None, sku=None): _matched(sku, i['sku']))] return all_images except KeyError: - raise CLIError('Could not retrieve image list from {}'.format(target_url)) + raise CLIError('Could not retrieve image list from {} or local copy'.format(target_url)) def load_extension_images_thru_services(cli_ctx, publisher, name, version, location, diff --git a/src/azure-cli/azure/cli/command_modules/vm/_alias.py b/src/azure-cli/azure/cli/command_modules/vm/_alias.py new file mode 100644 index 00000000000..62809bbfe83 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/vm/_alias.py @@ -0,0 +1,101 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +# Alias for image +# https://github.com/Azure/azure-rest-api-specs/blob/master/arm-compute/quickstart-templates/aliases.json + +alias_json = """ +{ + "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json", + "contentVersion": "1.0.0.0", + "parameters": {}, + "variables": {}, + "resources": [], + "outputs": { + "aliases": { + "type": "object", + "value": { + "Linux": { + "CentOS": { + "publisher": "OpenLogic", + "offer": "CentOS", + "sku": "7.5", + "version": "latest" + }, + "CoreOS": { + "publisher": "CoreOS", + "offer": "CoreOS", + "sku": "Stable", + "version": "latest" + }, + "Debian": { + "publisher": "Debian", + "offer": "debian-10", + "sku": "10", + "version": "latest" + }, + "openSUSE-Leap": { + "publisher": "SUSE", + "offer": "openSUSE-Leap", + "sku": "42.3", + "version": "latest" + }, + "RHEL": { + "publisher": "RedHat", + "offer": "RHEL", + "sku": "7-LVM", + "version": "latest" + }, + "SLES": { + "publisher": "SUSE", + "offer": "SLES", + "sku": "15", + "version": "latest" + }, + "UbuntuLTS": { + "publisher": "Canonical", + "offer": "UbuntuServer", + "sku": "18.04-LTS", + "version": "latest" + } + }, + "Windows": { + "Win2019Datacenter": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2019-Datacenter", + "version": "latest" + }, + "Win2016Datacenter": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2016-Datacenter", + "version": "latest" + }, + "Win2012R2Datacenter": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2012-R2-Datacenter", + "version": "latest" + }, + "Win2012Datacenter": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2012-Datacenter", + "version": "latest" + }, + "Win2008R2SP1": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2008-R2-SP1", + "version": "latest" + } + } + } + } + } +} + +""" diff --git a/src/azure-cli/azure/cli/command_modules/vm/_validators.py b/src/azure-cli/azure/cli/command_modules/vm/_validators.py index 83388d599bc..7fa554abd0a 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/_validators.py +++ b/src/azure-cli/azure/cli/command_modules/vm/_validators.py @@ -256,6 +256,7 @@ def _parse_image_argument(cmd, namespace): from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc import requests try: + images = None images = load_images_from_aliases_doc(cmd.cli_ctx) matched = next((x for x in images if x['urnAlias'].lower() == namespace.image.lower()), None) if matched: @@ -280,8 +281,8 @@ def _parse_image_argument(cmd, namespace): 'VHD blob URI, or pick an image from {}.\nSee vm create -h for more information ' \ 'on specifying an image.'.format(namespace.image, [x['urnAlias'] for x in images]) else: - err = 'Failed to connect to remote source of image aliases. Invalid image "{}". Use a ' \ - 'valid image URN, custom image name, custom image id, or VHD blob URI.\nSee vm ' \ + err = 'Failed to connect to remote source of image aliases or find a local copy. Invalid image "{}". ' \ + 'Use a valid image URN, custom image name, custom image id, or VHD blob URI.\nSee vm ' \ 'create -h for more information on specifying an image.'.format(namespace.image) raise CLIError(err)