diff --git a/scripts/move-tests.sh b/scripts/move-tests.sh index 272d07f2561..88a46bb1b1a 100755 --- a/scripts/move-tests.sh +++ b/scripts/move-tests.sh @@ -13,6 +13,14 @@ for test_folder in `find src/command_modules -name tests`; do cp -r $t/recordings/2017-03-09-profile/* $test_folder/profile_2017_03_09/recordings/ fi + if [ -d $t/recordings/2018-03-01-hybrid ]; then + mkdir -p $test_folder/hybrid_2018_03_01 + cp -r $t/* $test_folder/hybrid_2018_03_01 + rm -r $test_folder/hybrid_2018_03_01/recordings + mkdir -p $test_folder/hybrid_2018_03_01/recordings + cp -r $t/recordings/2018-03-01-hybrid/* $test_folder/hybrid_2018_03_01/recordings/ + fi + mkdir -p $test_folder/latest cp -r $t/* $test_folder/latest/ diff --git a/src/azure-cli-core/azure/cli/core/commands/arm.py b/src/azure-cli-core/azure/cli/core/commands/arm.py index 966cac15fc2..a7073b6700c 100644 --- a/src/azure-cli-core/azure/cli/core/commands/arm.py +++ b/src/azure-cli-core/azure/cli/core/commands/arm.py @@ -989,6 +989,7 @@ def _find_property(instance, path): def assign_identity(cli_ctx, getter, setter, identity_role=None, identity_scope=None): import time from msrestazure.azure_exceptions import CloudError + from azure.cli.core.profiles import get_api_version # get resource = getter() @@ -997,21 +998,31 @@ def assign_identity(cli_ctx, getter, setter, identity_role=None, identity_scope= # create role assignment: if identity_scope: principal_id = resource.identity.principal_id + version = getattr(get_api_version(cli_ctx, ResourceType.MGMT_AUTHORIZATION), 'role_assignments') identity_role_id = resolve_role_id(cli_ctx, identity_role, identity_scope) - assignments_client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_AUTHORIZATION).role_assignments - RoleAssignmentCreateParameters = get_sdk(cli_ctx, ResourceType.MGMT_AUTHORIZATION, - 'RoleAssignmentCreateParameters', mod='models', - operation_group='role_assignments') + RoleAssignmentCreateParameters, RoleAssignmentProperties = get_sdk(cli_ctx, ResourceType.MGMT_AUTHORIZATION, + 'RoleAssignmentCreateParameters', + 'RoleAssignmentProperties', mod='models', + operation_group='role_assignments') + parameters = RoleAssignmentCreateParameters(role_definition_id=identity_role_id, principal_id=principal_id) + assignments_client = get_mgmt_service_client(cli_ctx, ResourceType.MGMT_AUTHORIZATION).role_assignments logger.info("Creating an assignment with a role '%s' on the scope of '%s'", identity_role_id, identity_scope) retry_times = 36 assignment_name = _gen_guid() for l in range(0, retry_times): try: - assignments_client.create(scope=identity_scope, role_assignment_name=assignment_name, - parameters=parameters) + if version == '2015-07-01': + properties = RoleAssignmentProperties(role_definition_id=identity_role_id, + principal_id=principal_id) + assignments_client = assignments_client.create(scope=identity_scope, + role_assignment_name=assignment_name, + properties=properties) + else: + assignments_client.create(scope=identity_scope, role_assignment_name=assignment_name, + parameters=parameters) break except CloudError as ex: if 'role assignment already exists' in ex.message: diff --git a/src/azure-cli-core/azure/cli/core/profiles/__init__.py b/src/azure-cli-core/azure/cli/core/profiles/__init__.py index 9aadd655d3d..98831b7bd70 100644 --- a/src/azure-cli-core/azure/cli/core/profiles/__init__.py +++ b/src/azure-cli-core/azure/cli/core/profiles/__init__.py @@ -87,7 +87,8 @@ def get_sdk(cli_ctx, resource_type, *attr_args, **kwargs): # API Profiles currently supported in the CLI. API_PROFILES = { 'latest': AZURE_API_PROFILES['latest'], - '2017-03-09-profile': AZURE_API_PROFILES['2017-03-09-profile'] + '2017-03-09-profile': AZURE_API_PROFILES['2017-03-09-profile'], + '2018-03-01-hybrid': AZURE_API_PROFILES['2018-03-01-hybrid'] } diff --git a/src/azure-cli-core/azure/cli/core/profiles/_shared.py b/src/azure-cli-core/azure/cli/core/profiles/_shared.py index 2cd1e0a2b12..5c00ea1a49f 100644 --- a/src/azure-cli-core/azure/cli/core/profiles/_shared.py +++ b/src/azure-cli-core/azure/cli/core/profiles/_shared.py @@ -42,6 +42,7 @@ class ResourceType(Enum): # pylint: disable=too-few-public-methods MGMT_RESOURCE_LINKS = ('azure.mgmt.resource.links', 'ManagementLinkClient') MGMT_RESOURCE_LOCKS = ('azure.mgmt.resource.locks', 'ManagementLockClient') MGMT_RESOURCE_POLICY = ('azure.mgmt.resource.policy', 'PolicyClient') + MGMT_KEYVAULT = ('azure.mgmt.keyvault', 'KeyVaultManagementClient') MGMT_RESOURCE_RESOURCES = ('azure.mgmt.resource.resources', 'ResourceManagementClient') MGMT_RESOURCE_SUBSCRIPTIONS = ('azure.mgmt.resource.subscriptions', 'SubscriptionClient') DATA_KEYVAULT = ('azure.keyvault', 'KeyVaultClient') @@ -105,13 +106,11 @@ def default_api_version(self): ResourceType.MGMT_STORAGE: '2016-01-01', ResourceType.MGMT_NETWORK: '2015-06-15', ResourceType.MGMT_COMPUTE: SDKProfile('2016-03-30'), - ResourceType.MGMT_RESOURCE_FEATURES: '2015-12-01', ResourceType.MGMT_RESOURCE_LINKS: '2016-09-01', ResourceType.MGMT_RESOURCE_LOCKS: '2015-01-01', ResourceType.MGMT_RESOURCE_POLICY: '2015-10-01-preview', ResourceType.MGMT_RESOURCE_RESOURCES: '2016-02-01', ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2016-06-01', - ResourceType.DATA_STORAGE: '2015-04-05', ResourceType.MGMT_NETWORK_DNS: '2016-04-01', ResourceType.MGMT_AUTHORIZATION: SDKProfile('2015-07-01', { 'classic_administrators': '2015-06-01' @@ -119,6 +118,23 @@ def default_api_version(self): ResourceType.DATA_STORAGE: '2015-04-05', ResourceType.MGMT_KEYVAULT: '2016-10-01', ResourceType.DATA_KEYVAULT: '2016-10-01' + }, + '2018-03-01-hybrid': { + ResourceType.MGMT_STORAGE: '2016-01-01', + ResourceType.MGMT_NETWORK: '2017-10-01', + ResourceType.MGMT_COMPUTE: SDKProfile('2017-03-30'), + ResourceType.MGMT_RESOURCE_LINKS: '2018-02-01', + ResourceType.MGMT_RESOURCE_LOCKS: '2016-09-01', + ResourceType.MGMT_RESOURCE_POLICY: '2016-12-01', + ResourceType.MGMT_RESOURCE_RESOURCES: '2018-02-01', + ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS: '2016-06-01', + ResourceType.MGMT_AUTHORIZATION: SDKProfile('2015-07-01', { + 'classic_administrators': '2015-06-01' + }), + ResourceType.DATA_STORAGE: '2017-04-17', + ResourceType.DATA_COSMOS_TABLE: '2017-04-17', + ResourceType.MGMT_KEYVAULT: '2016-10-01', + ResourceType.DATA_KEYVAULT: '2016-10-01' } } diff --git a/src/azure-cli-core/azure/cli/core/tests/test_cloud.py b/src/azure-cli-core/azure/cli/core/tests/test_cloud.py index 810a5d23cb3..0c8be237bca 100644 --- a/src/azure-cli-core/azure/cli/core/tests/test_cloud.py +++ b/src/azure-cli-core/azure/cli/core/tests/test_cloud.py @@ -100,6 +100,26 @@ def test_add_get_cloud_with_profile(self): self.assertEqual(custom_clouds[0].endpoints.resource_manager, c.endpoints.resource_manager) self.assertEqual(custom_clouds[0].profile, c.profile) +def test_add_get_cloud_with_hybrid_profile(self): + cli = TestCli() + endpoint_rm = 'http://management.contoso.com' + endpoints = CloudEndpoints(resource_manager=endpoint_rm) + profile = '2018-03-01-hybrid' + c = Cloud('MyOwnCloud', endpoints=endpoints, profile=profile) + with mock.patch('azure.cli.core.cloud.CLOUD_CONFIG_FILE', tempfile.mkstemp()[1]) as\ + config_file: + add_cloud(cli, c) + config = cli.config.config_parser + config.read(config_file) + self.assertTrue(c.name in config.sections()) + self.assertEqual(config.get(c.name, 'endpoint_resource_manager'), endpoint_rm) + self.assertEqual(config.get(c.name, 'profile'), profile) + custom_clouds = get_custom_clouds(cli) + self.assertEqual(len(custom_clouds), 1) + self.assertEqual(custom_clouds[0].name, c.name) + self.assertEqual(custom_clouds[0].endpoints.resource_manager, c.endpoints.resource_manager) + self.assertEqual(custom_clouds[0].profile, c.profile) + def test_add_get_cloud_with_invalid_profile(self): # Cloud has profile that doesn't exist so an exception should be raised cli = DummyCli() diff --git a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py index 7703e82a205..fd4f7a4e03c 100644 --- a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py +++ b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/_validators.py @@ -303,6 +303,8 @@ def _validate_location(cmd, namespace, zone_info, size_info): if zone_info: sku_infos = list_sku_info(cmd.cli_ctx, namespace.location) temp = next((x for x in sku_infos if x.name.lower() == size_info.lower()), None) + if not hasattr(temp, 'location_info'): + return if not temp or not [x for x in (temp.location_info or []) if x.zones]: raise CLIError("{}'s location can't be used to create the VM/VMSS because availablity zone is not yet " "supported. Please use '--location' to specify a capable one. 'az vm list-skus' can be " diff --git a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/custom.py b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/custom.py index 2122df54f60..4477917a087 100644 --- a/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/custom.py +++ b/src/command_modules/azure-cli-vm/azure/cli/command_modules/vm/custom.py @@ -450,12 +450,16 @@ def setter(vm, external_identities=external_identities): for identity in external_identities: vm.identity.user_assigned_identities[identity] = VirtualMachineIdentityUserAssignedIdentitiesValue() - vm_patch = VirtualMachineUpdate() - vm_patch.identity = vm.identity - return patch_vm(cmd, resource_group_name, vm_name, vm_patch) + if VirtualMachineUpdate: + vm_patch = VirtualMachineUpdate() + vm_patch.identity = vm.identity + return patch_vm(cmd, resource_group_name, vm_name, vm_patch) + return set_vm(cmd, vm) assign_identity_helper(cmd.cli_ctx, getter, setter, identity_role=identity_role_id, identity_scope=identity_scope) vm = client.virtual_machines.get(resource_group_name, vm_name) + if not hasattr(vm.identity, 'user_assigned_identities'): + vm.identity.user_assigned_identities = None return _construct_identity_info(identity_scope, identity_role, vm.identity.principal_id, vm.identity.user_assigned_identities) @@ -665,6 +669,8 @@ def create_vm(cmd, vm_name, resource_group_name, image=None, size='Standard_DS1_ if assign_identity is not None: if enable_local_identity and not identity_scope: _show_missing_access_warning(resource_group_name, vm_name, 'vm') + if not hasattr(vm.identity, 'user_assigned_identities'): + vm.identity.user_assigned_identities = None setattr(vm, 'identity', _construct_identity_info(identity_scope, identity_role, vm.identity.principal_id, vm.identity.user_assigned_identities)) return vm @@ -1760,9 +1766,12 @@ def setter(vmss, external_identities=external_identities): vmss.identity.user_assigned_identities = {} for identity in external_identities: vmss.identity.user_assigned_identities[identity] = IdentityUserAssignedIdentitiesValue() - vmss_patch = VirtualMachineScaleSetUpdate() - vmss_patch.identity = vmss.identity - poller = client.virtual_machine_scale_sets.update(resource_group_name, vmss_name, vmss_patch) + if VirtualMachineScaleSetUpdate: + vmss_patch = VirtualMachineScaleSetUpdate() + vmss_patch.identity = vmss.identity + poller = client.virtual_machine_scale_sets.update(resource_group_name, vmss_name, vmss_patch) + else: + poller = client.virtual_machine_scale_sets.create_or_update(resource_group_name, vmss_name, vmss) return LongRunningOperation(cmd.cli_ctx)(poller) assign_identity_helper(cmd.cli_ctx, getter, setter, identity_role=identity_role_id, identity_scope=identity_scope) @@ -1771,6 +1780,8 @@ def setter(vmss, external_identities=external_identities): logger.warning("With manual upgrade mode, you will need to run 'az vmss update-instances -g %s -n %s " "--instance-ids *' to propagate the change", resource_group_name, vmss_name) + if not hasattr(vmss.identity, 'user_assigned_identities'): + vmss.identity.user_assigned_identities = None return _construct_identity_info(identity_scope, identity_role, vmss.identity.principal_id, vmss.identity.user_assigned_identities) @@ -2050,6 +2061,8 @@ def _get_public_ip_address_allocation(value, sku): vmss_info = get_vmss(cmd, resource_group_name, vmss_name) if enable_local_identity and not identity_scope: _show_missing_access_warning(resource_group_name, vmss_name, 'vmss') + if not hasattr(vmss_info.identity, 'user_assigned_identities'): + vmss_info.identity.user_assigned_identities = None deployment_result['vmss']['identity'] = _construct_identity_info(identity_scope, identity_role, vmss_info.identity.principal_id, vmss_info.identity.user_assigned_identities)