1# -------------------------------------------------------------------------------------------- 2# Copyright (c) Microsoft Corporation. All rights reserved. 3# Licensed under the MIT License. See License.txt in the project root for license information. 4# -------------------------------------------------------------------------------------------- 5 6import platform 7 8from azure.cli.core.commands.arm import resource_exists 9 10from knack.log import get_logger 11from knack.util import CLIError 12 13logger = get_logger(__name__) 14 15 16def get_folded_parameter_help_string( 17 display_name, allow_none=False, allow_new=False, default_none=False, 18 other_required_option=None, allow_cross_sub=True): 19 """ Assembles a parameterized help string for folded parameters. """ 20 quotes = '""' if platform.system() == 'Windows' else "''" 21 22 if default_none and not allow_none: 23 raise CLIError('Cannot use default_none=True and allow_none=False') 24 25 if not allow_new and not allow_none and not default_none: 26 help_text = 'Name or ID of an existing {}.'.format(display_name) 27 elif not allow_new and allow_none and not default_none: 28 help_text = 'Name or ID of an existing {}, or {} for none.'.format(display_name, quotes) 29 elif allow_new and not allow_none and not default_none: 30 help_text = 'Name or ID of the {}. Will create resource if it does not exist.'.format( 31 display_name) 32 elif allow_new and allow_none and not default_none: 33 help_text = 'Name or ID of the {}, or {} for none. Uses existing resource if available or will create a new ' \ 34 'resource with defaults if omitted.' 35 help_text = help_text.format(display_name, quotes) 36 elif not allow_new and allow_none and default_none: 37 help_text = 'Name or ID of an existing {}, or none by default.'.format(display_name) 38 elif allow_new and allow_none and default_none: 39 help_text = 'Name or ID of a {}. Uses existing resource or creates new if specified, or none if omitted.' 40 help_text = help_text.format(display_name) 41 42 # add parent name option string (if applicable) 43 if other_required_option: 44 help_text = '{} If name specified, also specify {}.'.format(help_text, other_required_option) 45 extra_sub_text = " or subscription" if allow_cross_sub else "" 46 help_text = '{} If you want to use an existing {display_name} in other resource group{append_sub}, ' \ 47 'please provide the ID instead of the name of the {display_name}'.format(help_text, 48 display_name=display_name, 49 append_sub=extra_sub_text) 50 return help_text 51 52 53def _validate_name_or_id( 54 cli_ctx, resource_group_name, property_value, property_type, parent_value, parent_type): 55 from azure.cli.core.commands.client_factory import get_subscription_id 56 from msrestazure.tools import parse_resource_id, is_valid_resource_id 57 has_parent = parent_type is not None 58 if is_valid_resource_id(property_value): 59 resource_id_parts = parse_resource_id(property_value) 60 value_supplied_was_id = True 61 elif has_parent: 62 resource_id_parts = dict( 63 name=parent_value, 64 resource_group=resource_group_name, 65 namespace=parent_type.split('/')[0], 66 type=parent_type.split('/')[1], 67 subscription=get_subscription_id(cli_ctx), 68 child_name_1=property_value, 69 child_type_1=property_type) 70 value_supplied_was_id = False 71 else: 72 resource_id_parts = dict( 73 name=property_value, 74 resource_group=resource_group_name, 75 namespace=property_type.split('/')[0], 76 type=property_type.split('/')[1], 77 subscription=get_subscription_id(cli_ctx)) 78 value_supplied_was_id = False 79 return (resource_id_parts, value_supplied_was_id) 80 81 82def get_folded_parameter_validator( 83 property_name, property_type, property_option, 84 parent_name=None, parent_type=None, parent_option=None, 85 allow_none=False, allow_new=False, default_none=False): 86 87 # Ensure that all parent parameters are specified if any are 88 parent_params = [parent_name, parent_type, parent_option] 89 has_parent = any(parent_params) 90 if has_parent and not all(parent_params): 91 raise CLIError('All parent parameters must be specified (name, type, option) if any are.') 92 93 if default_none and not allow_none: 94 raise CLIError('Cannot use default_none=True if allow_none=False') 95 96 # construct the validator 97 def validator(cmd, namespace): 98 from msrestazure.tools import resource_id 99 type_field_name = '{}_type'.format(property_name) 100 property_val = getattr(namespace, property_name, None) 101 parent_val = getattr(namespace, parent_name, None) if parent_name else None 102 103 # Check for the different scenarios (order matters) 104 # 1) provided value indicates None (pair of empty quotes) 105 if property_val in ('', '""', "''") or (property_val is None and default_none): 106 if not allow_none: 107 raise CLIError('{} cannot be None.'.format(property_option)) 108 setattr(namespace, type_field_name, 'none') 109 setattr(namespace, property_name, None) 110 if parent_name and parent_val: 111 logger.warning('Ignoring: %s %s', parent_option, parent_val) 112 setattr(namespace, parent_name, None) 113 return # SUCCESS 114 115 # Create a resource ID we can check for existence. 116 (resource_id_parts, value_was_id) = _validate_name_or_id( 117 cmd.cli_ctx, namespace.resource_group_name, property_val, property_type, parent_val, parent_type) 118 119 # 2) resource exists 120 if resource_exists(cmd.cli_ctx, **resource_id_parts): 121 setattr(namespace, type_field_name, 'existingId') 122 setattr(namespace, property_name, resource_id(**resource_id_parts)) 123 if parent_val: 124 if value_was_id: 125 logger.warning('Ignoring: %s %s', parent_option, parent_val) 126 setattr(namespace, parent_name, None) 127 return # SUCCESS 128 129 # if a parent name was required but not specified, raise a usage error 130 if has_parent and not value_was_id and not parent_val and not allow_new: 131 raise ValueError('incorrect usage: {0} ID | {0} NAME {1} NAME'.format( 132 property_option, parent_option)) 133 134 # if non-existent ID was supplied, throw error depending on whether a new resource can 135 # be created. 136 if value_was_id: 137 usage_message = '{} NAME'.format(property_option) if not has_parent \ 138 else '{} NAME [{} NAME]'.format(property_option, parent_option) 139 action_message = 'Specify ( {} ) to create a new resource.'.format(usage_message) if \ 140 allow_new else 'Create the required resource and try again.' 141 raise CLIError('{} {} does not exist. {}'.format( 142 property_name, property_val, action_message)) 143 144 # 3) try to create new resource 145 if allow_new: 146 setattr(namespace, type_field_name, 'new') 147 else: 148 raise CLIError( 149 '{} {} does not exist. Create the required resource and try again.'.format( 150 property_name, property_val)) 151 152 return validator 153