def test_when_alias_doc_is_missing(self, mock_get_active_cloud): from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc p = mock.PropertyMock(side_effect=CloudEndpointNotSetException) mock_cloud = mock.MagicMock() type(mock_cloud.endpoints).vm_image_alias_doc = p mock_get_active_cloud.return_value = mock_cloud # assert with self.assertRaises(CLIError): load_images_from_aliases_doc()
def test_when_alias_doc_is_missing(self, mock_get_active_cloud): from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc p = mock.PropertyMock(side_effect=CloudEndpointNotSetException) mock_cloud = mock.MagicMock() type(mock_cloud.endpoints).vm_image_alias_doc = p mock_get_active_cloud.return_value = mock_cloud # assert with self.assertRaises(CLIError): load_images_from_aliases_doc()
def _validate_vm_create_storage_profile(namespace): from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc image = namespace.image # 1 - Create native disk with VHD URI if image.lower().endswith('.vhd'): namespace.storage_profile = StorageProfile.SACustomImage if not namespace.os_type: raise CLIError('--os-type TYPE is required for a native OS VHD disk.') return # attempt to parse an URN urn_match = re.match('([^:]*):([^:]*):([^:]*):([^:]*)', image) if urn_match: namespace.os_publisher = urn_match.group(1) namespace.os_offer = urn_match.group(2) namespace.os_sku = urn_match.group(3) namespace.os_version = urn_match.group(4) else: images = load_images_from_aliases_doc() matched = next((x for x in images if x['urnAlias'].lower() == image.lower()), None) if matched is None: raise CLIError('Invalid image "{}". Please pick one from {}'.format( image, [x['urnAlias'] for x in images])) namespace.os_publisher = matched['publisher'] namespace.os_offer = matched['offer'] namespace.os_sku = matched['sku'] namespace.os_version = matched['version'] namespace.os_type = 'windows' if 'windows' in namespace.os_offer.lower() else 'linux' # 2 - Create native disk from PIR image namespace.storage_profile = StorageProfile.SAPirImage # pylint: disable=redefined-variable-type
def test_when_alias_doc_is_missing(self, mock_get_active_cloud): from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc p = mock.PropertyMock(side_effect=CloudEndpointNotSetException('')) mock_cloud = mock.MagicMock() type(mock_cloud.endpoints).vm_image_alias_doc = p mock_get_active_cloud.return_value = mock_cloud # assert cli_ctx = DummyCli() cli_ctx.cloud = mock_cloud images = load_images_from_aliases_doc(cli_ctx) self.assertEqual(images[0], {'urnAlias': 'CentOS', 'publisher': 'OpenLogic', 'offer': 'CentOS', 'sku': '7.5', 'version': 'latest'})
def _parse_image_argument(namespace): """ Systematically determines what type is supplied for the --image parameter. Updates the namespace and returns the type for subsequent processing. """ # 1 - easy check for URI if namespace.image.lower().endswith('.vhd'): return 'uri' # 2 - attempt to match an URN alias (most likely) from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc images = load_images_from_aliases_doc() matched = next( (x for x in images if x['urnAlias'].lower() == namespace.image.lower()), None) if matched: namespace.os_publisher = matched['publisher'] namespace.os_offer = matched['offer'] namespace.os_sku = matched['sku'] namespace.os_version = matched['version'] return 'urn' # 3 - attempt to match an URN pattern urn_match = re.match('([^:]*):([^:]*):([^:]*):([^:]*)', namespace.image) if urn_match: namespace.os_publisher = urn_match.group(1) namespace.os_offer = urn_match.group(2) namespace.os_sku = urn_match.group(3) namespace.os_version = urn_match.group(4) return 'urn' # 4 - check if a fully-qualified ID (assumes it is an image ID) if is_valid_resource_id(namespace.image): return 'image_id' # 5 - check if an existing managed disk image resource compute_client = _compute_client_factory() try: compute_client.images.get(namespace.resource_group_name, namespace.image) namespace.image = _get_resource_id(namespace.image, namespace.resource_group_name, 'images', 'Microsoft.Compute') return 'image_id' except CloudError: err = 'Invalid image "{}". Use a custom image name, id, or pick one from {}' raise CLIError( err.format(namespace.image, [x['urnAlias'] for x in images]))
def process_image_template_create_namespace(cmd, namespace): # pylint: disable=too-many-locals, too-many-branches, too-many-statements from azure.cli.core.commands.parameters import get_subscription_locations source = None scripts = [] # default location to RG location. if not namespace.location: get_default_location_from_resource_group(cmd, namespace) # validate tags. validate_tags(namespace) # Validate and parse scripts if namespace.scripts: for ns_script in namespace.scripts: scripts.append(_parse_script(ns_script)) # Validate and parse destination and locations destinations = [] subscription_locations = get_subscription_locations(cmd.cli_ctx) location_names = [l.name for l in subscription_locations] location_display_names = [l.display_name for l in subscription_locations] if namespace.managed_image_destinations: for dest in namespace.managed_image_destinations: rid, location = _parse_image_destination(cmd, namespace.resource_group_name, dest, is_shared_image=False) location = _validate_location(location, location_names, location_display_names) destinations.append((_DestType.MANAGED_IMAGE, rid, location)) if namespace.shared_image_destinations: for dest in namespace.shared_image_destinations: rid, locations = _parse_image_destination(cmd, namespace.resource_group_name, dest, is_shared_image=True) locations = [_validate_location(l, location_names, location_display_names) for l in locations] destinations.append((_DestType.SHARED_IMAGE_GALLERY, rid, locations)) # Validate and parse source image # 1 - check if source is a URN. A urn e.g "Canonical:UbuntuServer:18.04-LTS:latest" urn_match = re.match('([^:]*):([^:]*):([^:]*):([^:]*)', namespace.source) if urn_match: # if platform image urn source = { 'publisher': urn_match.group(1), 'offer': urn_match.group(2), 'sku': urn_match.group(3), 'version': urn_match.group(4), 'type': _SourceType.PLATFORM_IMAGE } likely_linux = bool("windows" not in source["offer"].lower() and "windows" not in source["sku"].lower()) logger.info("%s looks like a platform image URN", namespace.source) # 2 - check if a fully-qualified ID (assumes it is an image ID) elif is_valid_resource_id(namespace.source): parsed = parse_resource_id(namespace.source) image_type = parsed.get('type') image_resource_type = parsed.get('type') if not image_type: pass elif image_type.lower() == 'images': source = { 'image_id': namespace.source, 'type': _SourceType.MANAGED_IMAGE } logger.info("%s looks like a managed image id.", namespace.source) elif image_type == "galleries" and image_resource_type: source = { 'image_version_id': namespace.source, 'type': _SourceType.SIG_VERSION } logger.info("%s looks like a shared image version id.", namespace.source) # 3 - check if source is a Redhat iso uri. If so a checksum must be provided. elif urlparse(namespace.source).scheme and "://" in namespace.source and ".iso" in namespace.source.lower(): if not namespace.checksum: raise CLIError("Must provide a checksum for source uri: {}".format(namespace.source)) source = { 'source_uri': namespace.source, 'sha256_checksum': namespace.checksum, 'type': _SourceType.ISO_URI } likely_linux = True logger.info("%s looks like a RedHat iso uri.", namespace.source) # 4 - check if source is a urn alias from the vmImageAliasDoc endpoint. See "az cloud show" if not source: from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc images = load_images_from_aliases_doc(cmd.cli_ctx) matched = next((x for x in images if x['urnAlias'].lower() == namespace.source.lower()), None) if matched: source = { 'publisher': matched['publisher'], 'offer': matched['offer'], 'sku': matched['sku'], 'version': matched['version'], 'type': _SourceType.PLATFORM_IMAGE } if "windows" not in source["offer"].lower() and "windows" not in source["sku"].lower(): likely_linux = True logger.info("%s looks like a platform image alias.", namespace.source) # 5 - check if source is an existing managed disk image resource if not source: compute_client = _compute_client_factory(cmd.cli_ctx) try: image_name = namespace.source compute_client.images.get(namespace.resource_group_name, namespace.source) namespace.source = _get_resource_id(cmd.cli_ctx, namespace.source, namespace.resource_group_name, 'images', 'Microsoft.Compute') source = { 'image_id': namespace.source, 'type': _SourceType.MANAGED_IMAGE } logger.info("%s, looks like a managed image name. Using resource ID: %s", image_name, namespace.source) # pylint: disable=line-too-long except CloudError: pass if not source: err = 'Invalid image "{}". Use a valid image URN, managed image name or ID, ISO URI, ' \ 'or pick a platform image alias from {}.\nSee vm create -h for more information on specifying an image.'\ .format(namespace.source, ", ".join([x['urnAlias'] for x in images])) raise CLIError(err) for script in scripts: if script["type"] is None: try: script["type"] = ScriptType.SHELL if likely_linux else ScriptType.POWERSHELL logger.info("For script %s, likely linux is %s.", script["script"], likely_linux) except NameError: raise CLIError("Unable to infer the type of script {}.".format(script["script"])) namespace.source_dict = source namespace.scripts_list = scripts namespace.destinations_lists = destinations
def get_urn_aliases_completion_list(prefix, **kwargs): # pylint: disable=unused-argument images = load_images_from_aliases_doc() return [i['urnAlias'] for i in images]
def _parse_image_argument(namespace): """ Systematically determines what type is supplied for the --image parameter. Updates the namespace and returns the type for subsequent processing. """ # 1 - easy check for URI if namespace.image.lower().endswith('.vhd'): return 'uri' # 2 - attempt to match an URN alias (most likely) from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc images = load_images_from_aliases_doc() matched = next( (x for x in images if x['urnAlias'].lower() == namespace.image.lower()), None) if matched: namespace.os_publisher = matched['publisher'] namespace.os_offer = matched['offer'] namespace.os_sku = matched['sku'] namespace.os_version = matched['version'] return 'urn' # 3 - attempt to match an URN pattern urn_match = re.match('([^:]*):([^:]*):([^:]*):([^:]*)', namespace.image) if urn_match: namespace.os_publisher = urn_match.group(1) namespace.os_offer = urn_match.group(2) namespace.os_sku = urn_match.group(3) namespace.os_version = urn_match.group(4) try: compute_client = _compute_client_factory() if namespace.os_version.lower() == 'latest': top_one = compute_client.virtual_machine_images.list( namespace.location, namespace.os_publisher, namespace.os_offer, namespace.os_sku, top=1, orderby='name desc') if not top_one: raise CLIError("Can't resolve the vesion of '{}'".format( namespace.image)) image_version = top_one[0].name else: image_version = namespace.os_version image = compute_client.virtual_machine_images.get( namespace.location, namespace.os_publisher, namespace.os_offer, namespace.os_sku, image_version) # pylint: disable=no-member if image.plan: namespace.plan_name = image.plan.name namespace.plan_product = image.plan.product namespace.plan_publisher = image.plan.publisher except CloudError as ex: logger.warning( "Querying the image of '%s' failed for an error '%s'. Configuring plan settings " "will be skipped", namespace.image, ex.message) return 'urn' # 4 - check if a fully-qualified ID (assumes it is an image ID) if is_valid_resource_id(namespace.image): return 'image_id' # 5 - check if an existing managed disk image resource compute_client = _compute_client_factory() try: compute_client.images.get(namespace.resource_group_name, namespace.image) namespace.image = _get_resource_id(namespace.image, namespace.resource_group_name, 'images', 'Microsoft.Compute') return 'image_id' except CloudError: err = 'Invalid image "{}". Use a custom image name, id, or pick one from {}' raise CLIError( err.format(namespace.image, [x['urnAlias'] for x in images]))
def _parse_image_argument(namespace): """ Systematically determines what type is supplied for the --image parameter. Updates the namespace and returns the type for subsequent processing. """ # 1 - easy check for URI if namespace.image.lower().endswith('.vhd'): return 'uri' # 2 - attempt to match an URN alias (most likely) from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc images = load_images_from_aliases_doc() matched = next((x for x in images if x['urnAlias'].lower() == namespace.image.lower()), None) if matched: namespace.os_publisher = matched['publisher'] namespace.os_offer = matched['offer'] namespace.os_sku = matched['sku'] namespace.os_version = matched['version'] return 'urn' # 3 - attempt to match an URN pattern urn_match = re.match('([^:]*):([^:]*):([^:]*):([^:]*)', namespace.image) if urn_match: namespace.os_publisher = urn_match.group(1) namespace.os_offer = urn_match.group(2) namespace.os_sku = urn_match.group(3) namespace.os_version = urn_match.group(4) try: compute_client = _compute_client_factory() if namespace.os_version.lower() == 'latest': top_one = compute_client.virtual_machine_images.list(namespace.location, namespace.os_publisher, namespace.os_offer, namespace.os_sku, top=1, orderby='name desc') if not top_one: raise CLIError("Can't resolve the vesion of '{}'".format(namespace.image)) image_version = top_one[0].name else: image_version = namespace.os_version image = compute_client.virtual_machine_images.get(namespace.location, namespace.os_publisher, namespace.os_offer, namespace.os_sku, image_version) # pylint: disable=no-member if image.plan: namespace.plan_name = image.plan.name namespace.plan_product = image.plan.product namespace.plan_publisher = image.plan.publisher except CloudError as ex: logger.warning("Querying the image of '%s' failed for an error '%s'. Configuring plan settings " "will be skipped", namespace.image, ex.message) return 'urn' # 4 - check if a fully-qualified ID (assumes it is an image ID) if is_valid_resource_id(namespace.image): return 'image_id' # 5 - check if an existing managed disk image resource compute_client = _compute_client_factory() try: compute_client.images.get(namespace.resource_group_name, namespace.image) namespace.image = _get_resource_id(namespace.image, namespace.resource_group_name, 'images', 'Microsoft.Compute') return 'image_id' except CloudError: err = 'Invalid image "{}". Use a custom image name, id, or pick one from {}' raise CLIError(err.format(namespace.image, [x['urnAlias'] for x in images]))
def get_urn_aliases_completion_list(prefix, **kwargs): # pylint: disable=unused-argument images = load_images_from_aliases_doc() return [i['urnAlias'] for i in images]
def _validate_vm_create_storage_profile(namespace, for_scale_set=False): # pylint: disable=too-many-branches, too-many-statements from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc image = namespace.image or '' # do VM specific validating if not for_scale_set: if namespace.managed_os_disk: if namespace.image: raise CLIError( "'--image' is not applicable when attach to an existing os disk" ) if not namespace.os_type: raise CLIError( '--os-type TYPE is required when attach to an existing os disk' ) else: if not image: raise CLIError("Please provide parameter value to '--image'") if image.lower().endswith('.vhd'): if not namespace.os_type: raise CLIError( '--os-type TYPE is required for a native OS VHD disk.') valid_managed_skus = ['premium_lrs', 'standard_lrs'] valid_unmanaged_skus = valid_managed_skus + [ 'standard_grs', 'standard_ragrs', 'standard_zrs' ] if namespace.use_unmanaged_disk: if namespace.storage_sku.lower() not in valid_unmanaged_skus: raise CLIError( "Invalid storage sku '{}', please choose from '{}'".format( namespace.storage_sku, valid_unmanaged_skus)) if namespace.data_disk_sizes_gb: raise CLIError( "'--data-disk-sizes-gb' is only applicable when use managed disks" ) if '/images/' in namespace.image.lower(): raise CLIError( "VM/VMSS created from a managed custom image must use managed disks" ) if not for_scale_set and namespace.managed_os_disk: raise CLIError( "'--use-unmanaged-disk' is ignored when attach to a managed os disk" ) else: if namespace.storage_sku.lower() not in valid_managed_skus: err = "invalid storage sku '{}' to use for managed os disks, please choose from '{}'" raise CLIError( err.format(namespace.storage_sku, valid_managed_skus)) if for_scale_set and namespace.os_disk_name: raise CLIError( "'--os-disk-name' is not allowed for scale sets using managed disks" ) if not for_scale_set and namespace.storage_account: raise CLIError( "'--storage-account' is only applicable when use unmanaged disk." " Please either remove it or turn on '--use-unmanaged-disk'") # attempt to parse an URN urn_match = re.match('([^:]*):([^:]*):([^:]*):([^:]*)', image) if urn_match: namespace.os_publisher = urn_match.group(1) namespace.os_offer = urn_match.group(2) namespace.os_sku = urn_match.group(3) namespace.os_version = urn_match.group(4) namespace.storage_profile = (StorageProfile.SAPirImage if namespace.use_unmanaged_disk else StorageProfile.ManagedPirImage) elif is_valid_resource_id(image): namespace.storage_profile = StorageProfile.ManagedCustomImage elif image.lower().endswith('.vhd'): # pylint: disable=redefined-variable-type namespace.storage_profile = StorageProfile.SACustomImage elif not for_scale_set and namespace.managed_os_disk: res = parse_resource_id(namespace.managed_os_disk) name = res['name'] rg = res.get('resource_group', namespace.resource_group_name) namespace.managed_os_disk = resource_id( subscription=get_subscription_id(), resource_group=rg, namespace='Microsoft.Compute', type='disks', name=name) namespace.storage_profile = StorageProfile.ManagedSpecializedOSDisk else: images = load_images_from_aliases_doc() matched = next( (x for x in images if x['urnAlias'].lower() == image.lower()), None) if matched: namespace.os_publisher = matched['publisher'] namespace.os_offer = matched['offer'] namespace.os_sku = matched['sku'] namespace.os_version = matched['version'] namespace.storage_profile = (StorageProfile.SAPirImage if namespace.use_unmanaged_disk else StorageProfile.ManagedPirImage) else: # last try: is it a custom image name? compute_client = _compute_client_factory() try: compute_client.images.get(namespace.resource_group_name, image) image = namespace.image = _get_resource_id( image, namespace.resource_group_name, 'images', 'Microsoft.Compute') namespace.storage_profile = StorageProfile.ManagedCustomImage except CloudError: err = 'Invalid image "{}". Use a custom image name, id, or pick one from {}' raise CLIError( err.format(image, [x['urnAlias'] for x in images])) if namespace.storage_profile == StorageProfile.ManagedCustomImage: res = parse_resource_id(image) compute_client = _compute_client_factory() image_info = compute_client.images.get(res['resource_group'], res['name']) # pylint: disable=no-member namespace.os_type = image_info.storage_profile.os_disk.os_type.value namespace.image_data_disks = image_info.storage_profile.data_disks if not namespace.os_type: namespace.os_type = 'windows' if 'windows' in namespace.os_offer.lower( ) else 'linux'
def get_urn_aliases_completion_list(cmd, prefix, namespace): # pylint: disable=unused-argument images = load_images_from_aliases_doc(cmd.cli_ctx) return [i['urnAlias'] for i in images]
def _validate_vm_create_storage_profile(namespace, for_scale_set=False): # pylint: disable=too-many-branches, too-many-statements from azure.cli.command_modules.vm._actions import load_images_from_aliases_doc image = namespace.image or '' # do VM specific validating if not for_scale_set: if namespace.managed_os_disk: if namespace.image: raise CLIError("'--image' is not applicable when attach to an existing os disk") if not namespace.os_type: raise CLIError('--os-type TYPE is required when attach to an existing os disk') else: if not image: raise CLIError("Please provide parameter value to '--image'") if image.lower().endswith('.vhd'): if not namespace.os_type: raise CLIError('--os-type TYPE is required for a native OS VHD disk.') valid_managed_skus = ['premium_lrs', 'standard_lrs'] valid_unmanaged_skus = valid_managed_skus + ['standard_grs', 'standard_ragrs', 'standard_zrs'] if namespace.use_unmanaged_disk: if namespace.storage_sku.lower() not in valid_unmanaged_skus: raise CLIError("Invalid storage sku '{}', please choose from '{}'".format( namespace.storage_sku, valid_unmanaged_skus)) if namespace.data_disk_sizes_gb: raise CLIError("'--data-disk-sizes-gb' is only applicable when use managed disks") if '/images/' in namespace.image.lower(): raise CLIError("VM/VMSS created from a managed custom image must use managed disks") if not for_scale_set and namespace.managed_os_disk: raise CLIError("'--use-unmanaged-disk' is ignored when attach to a managed os disk") else: if namespace.storage_sku.lower() not in valid_managed_skus: err = "invalid storage sku '{}' to use for managed os disks, please choose from '{}'" raise CLIError(err.format(namespace.storage_sku, valid_managed_skus)) if for_scale_set and namespace.os_disk_name: raise CLIError("'--os-disk-name' is not allowed for scale sets using managed disks") if not for_scale_set and namespace.storage_account: raise CLIError("'--storage-account' is only applicable when use unmanaged disk." " Please either remove it or turn on '--use-unmanaged-disk'") # attempt to parse an URN urn_match = re.match('([^:]*):([^:]*):([^:]*):([^:]*)', image) if urn_match: namespace.os_publisher = urn_match.group(1) namespace.os_offer = urn_match.group(2) namespace.os_sku = urn_match.group(3) namespace.os_version = urn_match.group(4) namespace.storage_profile = (StorageProfile.SAPirImage if namespace.use_unmanaged_disk else StorageProfile.ManagedPirImage) elif is_valid_resource_id(image): namespace.storage_profile = StorageProfile.ManagedCustomImage elif image.lower().endswith('.vhd'): # pylint: disable=redefined-variable-type namespace.storage_profile = StorageProfile.SACustomImage elif not for_scale_set and namespace.managed_os_disk: res = parse_resource_id(namespace.managed_os_disk) name = res['name'] rg = res.get('resource_group', namespace.resource_group_name) namespace.managed_os_disk = resource_id( subscription=get_subscription_id(), resource_group=rg, namespace='Microsoft.Compute', type='disks', name=name) namespace.storage_profile = StorageProfile.ManagedSpecializedOSDisk else: images = load_images_from_aliases_doc() matched = next((x for x in images if x['urnAlias'].lower() == image.lower()), None) if matched: namespace.os_publisher = matched['publisher'] namespace.os_offer = matched['offer'] namespace.os_sku = matched['sku'] namespace.os_version = matched['version'] namespace.storage_profile = (StorageProfile.SAPirImage if namespace.use_unmanaged_disk else StorageProfile.ManagedPirImage) else: # last try: is it a custom image name? compute_client = _compute_client_factory() try: compute_client.images.get(namespace.resource_group_name, image) image = namespace.image = _get_resource_id(image, namespace.resource_group_name, 'images', 'Microsoft.Compute') namespace.storage_profile = StorageProfile.ManagedCustomImage except CloudError: err = 'Invalid image "{}". Use a custom image name, id, or pick one from {}' raise CLIError(err.format(image, [x['urnAlias'] for x in images])) if namespace.storage_profile == StorageProfile.ManagedCustomImage: res = parse_resource_id(image) compute_client = _compute_client_factory() image_info = compute_client.images.get(res['resource_group'], res['name']) # pylint: disable=no-member namespace.os_type = image_info.storage_profile.os_disk.os_type.value namespace.image_data_disks = image_info.storage_profile.data_disks if not namespace.os_type: namespace.os_type = 'windows' if 'windows' in namespace.os_offer.lower() else 'linux'