Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/azure/cli/commands/arm.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ def set_properties(instance, expression):
else:
setattr(instance, name, value)
except IndexError:
raise CLIError('index {} doesn\'t exist on {}'.format(index_value, _make_camel_case(name)))
raise CLIError('index {} doesn\'t exist on {}'.format(index_value, make_camel_case(name)))
except (AttributeError, KeyError):
show_options(instance, name, key.split('.'))

Expand Down Expand Up @@ -301,26 +301,26 @@ def remove_properties(instance, argument_values):
except IndexError:
raise CLIError('index {} doesn\'t exist on {}'
.format(list_index,
_make_camel_case(list_attribute_path[-1])))
make_camel_case(list_attribute_path[-1])))

def show_options(instance, part, path):
options = instance.__dict__ if hasattr(instance, '__dict__') else instance
options = options.keys() if isinstance(options, dict) else options
options = [_make_camel_case(x) for x in options]
options = [make_camel_case(x) for x in options]
raise CLIError('Couldn\'t find "{}" in "{}". Available options: {}'
.format(_make_camel_case(part),
_make_camel_case('.'.join(path[:-1]).replace('.[', '[')),
.format(make_camel_case(part),
make_camel_case('.'.join(path[:-1]).replace('.[', '[')),
sorted(list(options), key=str)))

snake_regex_1 = re.compile('(.)([A-Z][a-z]+)')
snake_regex_2 = re.compile('([a-z0-9])([A-Z])')
def _make_snake_case(s):
def make_snake_case(s):
if isinstance(s, str):
s1 = re.sub(snake_regex_1, r'\1_\2', s)
return re.sub(snake_regex_2, r'\1_\2', s1).lower()
return s

def _make_camel_case(s):
def make_camel_case(s):
if isinstance(s, str):
parts = s.split('_')
return parts[0].lower() + ''.join(p.capitalize() for p in parts[1:])
Expand All @@ -332,7 +332,7 @@ def _get_internal_path(path):
_path = path.split('.') \
if '.[' in path \
else path.replace('[', '.[').split('.')
return [_make_snake_case(x) for x in _path]
return [make_snake_case(x) for x in _path]

def _get_name_path(path):
pathlist = _get_internal_path(path)
Expand All @@ -347,7 +347,7 @@ def _update_instance(instance, part, path):
instance = instance[index_value]
except IndexError:
raise CLIError('index {} doesn\'t exist on {}'.format(index_value,
_make_camel_case(path[-2])))
make_camel_case(path[-2])))
elif isinstance(instance, dict):
instance = instance[part]
else:
Expand Down
8 changes: 6 additions & 2 deletions src/azure/cli/commands/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#---------------------------------------------------------------------------------------------

import argparse
import platform

# pylint: disable=line-too-long
from azure.cli.commands import CliArgumentType, register_cli_argument
Expand Down Expand Up @@ -83,16 +84,19 @@ def completer(prefix, action, parsed_args, **kwargs): # pylint: disable=unused-a
validator=generate_deployment_name
)

quotes = '""' if platform.system() == 'Windows' else "''"
quote_text = 'Use {} for none.'.format(quotes)

tags_type = CliArgumentType(
type=validate_tags,
help='multiple semicolon separated tags in \'key[=value]\' format. Omit value to clear tags.',
help='multiple semicolon separated tags in \'key[=value]\' format. {}'.format(quote_text),
nargs='?',
const=''
)

tag_type = CliArgumentType(
type=validate_tag,
help='a single tag in \'key[=value]\' format. Omit value to clear tags.',
help='a single tag in \'key[=value]\' format. {}'.format(quote_text),
nargs='?',
const=''
)
Expand Down
32 changes: 25 additions & 7 deletions src/azure/cli/commands/template_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@

from __future__ import print_function
import argparse
import platform

from azure.cli._util import CLIError
from azure.cli.commands import register_cli_argument
from azure.cli.commands.arm import (
is_valid_resource_id,
parse_resource_id,
resource_id,
resource_exists)
resource_exists,
make_camel_case)

def register_folded_cli_argument(scope, base_name, resource_type, parent_name=None, # pylint: disable=too-many-arguments
parent_type=None, type_field=None,
existing_id_flag_value='existingId', new_flag_value='new',
none_flag_value='none', **kwargs):
none_flag_value='none', default_value_flag='new', allow_none=True,
**kwargs):
type_field_name = type_field or base_name + '_type'

fold_validator = _name_id_fold(base_name,
Expand All @@ -26,6 +29,7 @@ def register_folded_cli_argument(scope, base_name, resource_type, parent_name=No
existing_id_flag_value,
new_flag_value,
none_flag_value,
allow_none,
parent_name,
parent_type)
custom_validator = kwargs.pop('validator', None)
Expand All @@ -37,12 +41,22 @@ def wrapped(namespace):
else:
validator = fold_validator

register_cli_argument(scope, base_name, validator=validator, **kwargs)
quotes = '""' if platform.system() == 'Windows' else "''"
quote_text = ' Use {} for none.'.format(quotes) if allow_none else ''
flag_texts = {
new_flag_value: ' Creates new by default.{}'.format(quote_text),
existing_id_flag_value: ' Uses existing by default or creates if none found.{}'
.format(quote_text),
none_flag_value: ' None by default.'
}
help_text = 'Name or ID of the resource.' + flag_texts[default_value_flag]

register_cli_argument(scope, base_name, validator=validator, help=help_text, **kwargs)
register_cli_argument(scope, type_field_name, help=argparse.SUPPRESS, default=None)

def _name_id_fold(base_name, resource_type, type_field, #pylint: disable=too-many-arguments
existing_id_flag_value, new_flag_value, none_flag_value, parent_name=None,
parent_type=None):
existing_id_flag_value, new_flag_value, none_flag_value, allow_none=True,
parent_name=None, parent_type=None):
def handle_folding(namespace):
base_name_val = getattr(namespace, base_name)
type_field_val = getattr(namespace, type_field)
Expand All @@ -52,10 +66,14 @@ def handle_folding(namespace):
# Either no name was specified, or the user specified the type of resource
# (i.e. new/existing/none)
pass
elif base_name_val == '':
elif base_name_val in ('', '""', "''"):
# An empty name specified - that means that we are neither referencing an existing
# field, or the name is set to an empty string
# field, or the name is set to an empty string. We check for all types of quotes
# so scripts can run cross-platform.
if not allow_none:
raise CLIError('Field {} cannot be none.'.format(make_camel_case(base_name)))
setattr(namespace, type_field, none_flag_value)
setattr(namespace, base_name, None)
else:
from azure.cli.commands.client_factory import get_subscription_id
has_parent = parent_name is not None and parent_type is not None
Expand Down
9 changes: 5 additions & 4 deletions src/azure/cli/utils/vcr_test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ def _execute_playback(self):

def cmd(self, command, checks=None, allowed_exceptions=None, debug=False): #pylint: disable=no-self-use
allowed_exceptions = allowed_exceptions or []
if not isinstance(allowed_exceptions, list):
allowed_exceptions = [allowed_exceptions]

if self._debug or debug:
print('\n\tRUNNING: {}'.format(command))
command_list = shlex.split(command)
Expand All @@ -268,10 +271,8 @@ def cmd(self, command, checks=None, allowed_exceptions=None, debug=False): #pyli
cli_main(command_list, file=output)
except Exception as ex: # pylint: disable=broad-except
ex_msg = str(ex)
if not isinstance(allowed_exceptions, list):
allowed_exceptions = [allowed_exceptions]
if not next((x for x in allowed_exceptions if x in ex_msg), None):
raise ex
if not next((x for x in allowed_exceptions if x in ex_msg), None):
raise ex
self._track_executed_commands(command_list)
result = output.getvalue().strip()
output.close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ def __init__(self, test_method):
super(RoleCreateScenarioTest, self).__init__(__file__, test_method)

def test_role_create_scenario(self):
if self.playback:
return #live-only test, so far unable to replace guid in binary encoded body
self.execute()

def body(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,16 @@ def get_vm_size_completion_list(prefix, action, parsed_args, **kwargs):#pylint:
register_cli_argument(scope, 'ssh_dest_key_path', completer=FilesCompleter())
register_cli_argument(scope, 'dns_name_for_public_ip', CliArgumentType(action=VMDNSNameAction), options_list=('--public-ip-address-dns-name',), help='Globally unique DNS Name for the Public IP.')
register_cli_argument(scope, 'authentication_type', authentication_type)
register_folded_cli_argument(scope, 'availability_set', 'Microsoft.Compute/availabilitySets')
register_folded_cli_argument(scope, 'availability_set', 'Microsoft.Compute/availabilitySets', default_value_flag='none')
register_cli_argument(scope, 'private_ip_address_allocation', help=argparse.SUPPRESS)
register_cli_argument(scope, 'virtual_network_ip_address_prefix', options_list=('--vnet-ip-address-prefix',))
register_cli_argument(scope, 'subnet_ip_address_prefix', options_list=('--subnet-ip-address-prefix',))
register_cli_argument(scope, 'private_ip_address', help='Static private IP address (e.g. 10.0.0.5).', options_list=('--private-ip-address',), action=PrivateIpAction)
register_cli_argument(scope, 'public_ip_address_allocation', CliArgumentType(choices=['dynamic', 'static'], help='', default='dynamic', type=str.lower))
register_folded_cli_argument(scope, 'public_ip_address', 'Microsoft.Network/publicIPAddresses', help='Name or ID of public IP address (creates if doesn\'t exist)')
register_folded_cli_argument(scope, 'storage_account', 'Microsoft.Storage/storageAccounts', help='Name or ID of storage account (creates if doesn\'t exist). Chooses an existing storage account if none specified.', validator=_find_default_storage_account)
register_folded_cli_argument(scope, 'virtual_network', 'Microsoft.Network/virtualNetworks', help='Name or ID of virtual network (creates if doesn\'t exist). Chooses an existing VNet if none specified.', options_list=('--vnet',), validator=_find_default_vnet)
register_folded_cli_argument(scope, 'network_security_group', 'Microsoft.Network/networkSecurityGroups', help='Name or ID of network security group (creates if doesn\'t exist)', options_list=('--nsg',))
register_folded_cli_argument(scope, 'load_balancer', 'Microsoft.Network/loadBalancers', help='Name or ID of load balancer (creates if doesn\'t exist)')
register_folded_cli_argument(scope, 'public_ip_address', 'Microsoft.Network/publicIPAddresses')
register_folded_cli_argument(scope, 'storage_account', 'Microsoft.Storage/storageAccounts', validator=_find_default_storage_account, allow_none=False, default_value_flag='existingId')
register_folded_cli_argument(scope, 'virtual_network', 'Microsoft.Network/virtualNetworks', options_list=('--vnet',), validator=_find_default_vnet, allow_none=False, default_value_flag='existingId')
register_folded_cli_argument(scope, 'network_security_group', 'Microsoft.Network/networkSecurityGroups', options_list=('--nsg',))
register_folded_cli_argument(scope, 'load_balancer', 'Microsoft.Network/loadBalancers')
register_cli_argument(scope, 'network_security_group_rule', nsg_rule_type, options_list=('--nsg-rule',))
register_extra_cli_argument(scope, 'image', options_list=('--image',), action=VMImageFieldAction, completer=get_urn_aliases_completion_list, default='Win2012R2Datacenter')
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ Size Standard_A3, existing storage account, existing storage container name, exi
- delete test_vm_create_multinic.yaml
- re-record test

**Minimum VM**

- create vm with no availability set, NSG, public ip or tags
- verify create succeeds and that the other resources aren't created

OR

- delete test_vm_create_none_options.yaml
- re-record test

**custom Linux image**

- create VM, add a customization such as "sudo apt-get install emacs23"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,14 +350,14 @@
"existingId": "[parameters('publicIpAddress')]",
"existingName": "[concat('/subscriptions/',subscription().subscriptionId,'/resourceGroups/',resourceGroup().name,'/providers/Microsoft.Network/publicIPAddresses/',parameters('publicIpAddress'))]",
"new": "[concat('/subscriptions/',subscription().subscriptionId,'/resourceGroups/',resourceGroup().name,'/providers/Microsoft.Network/publicIPAddresses/',parameters('publicIpAddress'))]",
"none": null
"none": ""
},
"publicIpAddressId": "[variables('publicIpAddressIds')[parameters('publicIpAddressType')]]",
"loadBalancerIds": {
"existingId": "[parameters('loadBalancer')]",
"existingName": "[concat('/subscriptions/',subscription().subscriptionId,'/resourceGroups/',resourceGroup().name,'/providers/Microsoft.Network/loadBalancers/',parameters('loadBalancer'))]",
"new": "[concat('/subscriptions/',subscription().subscriptionId,'/resourceGroups/',resourceGroup().name,'/providers/Microsoft.Network/loadBalancers/',parameters('loadBalancer'))]",
"none": null
"none": ""
},
"loadBalancerId": "[variables('loadBalancerIds')[parameters('loadBalancerType')]]",
"subnetName": "[parameters('subnetName')]",
Expand Down Expand Up @@ -611,9 +611,9 @@
"parameters": {
"loadBalancerName": { "value": "[parameters('loadBalancer')]" },
"location": { "value": "[variables('resourceLocation')]" },
"publicIpAddress": { "value": "[variables('publicIPAddressID')]" },
"backendPoolName": { "value": "[variables('bePoolName')]" },
"publicIpAddress": { "value": "[variables('publicIpAddressId')]" },
"publicIpAddressType": { "value": "[variables('lbPublicIpAddressType')[parameters('publicIpAddressType')]]" },
"backendPoolName": { "value": "[variables('bePoolName')]" },
"subnet": { "value": "[variables('subnetRef')]" }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ OR
- Delete test_vm_scaleset_create_existing_options.yaml
- Re-record tests

**Minimum VMSS**

- create vmss with no load balancer, public ip or tags
- verify create succeeds and that the other resources aren't created

OR

- delete test_vmss_create_none_options.yaml
- re-record test

**custom Linux image**

- create VM1, add a customization such as "sudo apt-get install emacs23"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ class DeploymentVmss(Model):
sending a request.

:ivar uri: URI referencing the template. Default value:
"https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-02/azuredeploy.json"
"https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-04/azuredeploy.json"
.
:vartype uri: str
:param content_version: If included it must match the ContentVersion in
the template.
:type content_version: str
:ivar _artifacts_location: Container URI of of the template. Default
value:
"https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-02"
"https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-04"
.
:vartype _artifacts_location: str
:param admin_password: Password for the Virtual Machine. Required if SSH
Expand Down Expand Up @@ -207,9 +207,9 @@ class DeploymentVmss(Model):
'mode': {'key': 'properties.mode', 'type': 'str'},
}

uri = "https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-02/azuredeploy.json"
uri = "https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-04/azuredeploy.json"

_artifacts_location = "https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-02"
_artifacts_location = "https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-04"

mode = "Incremental"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class TemplateLink(Model):
sending a request.

:ivar uri: URI referencing the template. Default value:
"https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-02/azuredeploy.json"
"https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-04/azuredeploy.json"
.
:vartype uri: str
:param content_version: If included it must match the ContentVersion in
Expand All @@ -39,7 +39,7 @@ class TemplateLink(Model):
'content_version': {'key': 'contentVersion', 'type': 'str'},
}

uri = "https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-02/azuredeploy.json"
uri = "https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-04/azuredeploy.json"

def __init__(self, content_version=None):
self.content_version = content_version
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@
"type": "string",
"description": "URI referencing the template.",
"enum": [
"https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-02/azuredeploy.json"
"https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-04/azuredeploy.json"
]
},
"contentVersion": {
Expand Down Expand Up @@ -820,7 +820,7 @@
"description": "Container URI of of the template.",
"x-ms-client-name": "_artifactsLocation",
"enum": [
"https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-02"
"https://azuresdkci.blob.core.windows.net/templatehost/CreateVmss_2016-08-04"
]
}
},
Expand Down
Loading