Skip to content
Open
33 changes: 32 additions & 1 deletion src/azure-cli/azure/cli/command_modules/vm/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
validate_asg_names_or_ids, validate_keyvault, _validate_proximity_placement_group,
validate_vm_name_for_monitor_metrics)

from azure.cli.command_modules.vm._vm_utils import MSI_LOCAL_ID, CachingTypes, UpgradeMode
from azure.cli.command_modules.vm._vm_utils import (MSI_LOCAL_ID, CachingTypes, UpgradeMode, DiskStorageAlignment,
FaultDomainAlignment)
from azure.cli.command_modules.vm._image_builder import ScriptType

from azure.cli.command_modules.monitor.validators import validate_metric_dimension
Expand Down Expand Up @@ -509,6 +510,18 @@ def load_arguments(self, _):
c.argument('source_snapshots_or_disks_size_gb', options_list=['--source-snapshots-or-disks-size-gb', '--source-resource-size'], nargs='+', type=int, help='The size of the source disk in GB')
c.argument('source_disk_restore_point', options_list=['--source-disk-restore-point', '--source-disk-rp'], nargs='+', help='create a data disk from a disk restore point. Can use the ID of a disk restore point.')
c.argument('source_disk_restore_point_size_gb', options_list=['--source-disk-restore-point-size-gb', '--source-rp-size'], nargs='+', type=int, help='The size of the source disk restore point in GB')
c.argument(
'data_disk_storage_fault_domain_alignment',
options_list=['--data-disk-storage-fd-alignment', '--data-disk-fda'],
arg_type=get_enum_type(DiskStorageAlignment),
help='Specifies the storage fault domain alignment type for the disk.'
)
c.argument(
'os_disk_storage_fault_domain_alignment',
options_list=['--os-disk-storage-fd-alignment', '--os-disk-fda'],
arg_type=get_enum_type(DiskStorageAlignment),
help='Specifies the storage fault domain alignment type for the disk.'
)

with self.argument_context('vm create', arg_group='Dedicated Host', min_api='2019-03-01') as c:
c.argument('dedicated_host_group', options_list=['--host-group'], is_preview=True, help="Name or resource ID of the dedicated host group that the VM will reside in. --host and --host-group can't be used together.")
Expand Down Expand Up @@ -839,6 +852,24 @@ def load_arguments(self, _):
'to a single availability zone in the virtual machine scale set. '
'Valid values are integers between 1 and 100.'
)
c.argument(
'data_disk_storage_fault_domain_alignment',
options_list=['--data-disk-storage-fd-alignment', '--data-disk-fda'],
arg_type=get_enum_type(DiskStorageAlignment),
help='Specifies the storage fault domain alignment type for the disk.'
)
c.argument(
'os_disk_storage_fault_domain_alignment',
options_list=['--os-disk-storage-fd-alignment', '--os-disk-fda'],
arg_type=get_enum_type(DiskStorageAlignment),
help='Specifies the storage fault domain alignment type for the disk.'
)
c.argument(
'zonal_platform_fault_domain_align_mode',
options_list=['--zonal-fault-domain-align-mode', '--zonal-fda'],
arg_type=get_enum_type(FaultDomainAlignment),
Comment on lines +858 to +870
help='Specifies the align mode between Virtual Machine Scale Set compute and storage Fault Domain count.'
)

with self.argument_context('vmss create', arg_group='Network Balancer') as c:
c.argument('application_gateway', help='Name to use when creating a new application gateway (default) or referencing an existing one. Can also reference an existing application gateway by ID or specify "" for none.', options_list=['--app-gateway'])
Expand Down
20 changes: 18 additions & 2 deletions src/azure-cli/azure/cli/command_modules/vm/_template_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@ def build_vm_resource( # pylint: disable=too-many-locals, too-many-statements,
zone_placement_policy=None, include_zones=None, exclude_zones=None, align_regional_disks_to_vm_zone=None,
wire_server_mode=None, imds_mode=None, wire_server_access_control_profile_reference_id=None,
imds_access_control_profile_reference_id=None, key_incarnation_id=None, add_proxy_agent_extension=None,
disk_iops_read_write=None, disk_mbps_read_write=None, zone_movement=None):
disk_iops_read_write=None, disk_mbps_read_write=None, zone_movement=None,
os_disk_storage_fault_domain_alignment=None, data_disk_storage_fault_domain_alignment=None):

os_caching = disk_info['os'].get('caching')

Expand Down Expand Up @@ -561,6 +562,8 @@ def _build_storage_profile():
profile['osDisk']['writeAcceleratorEnabled'] = disk_info['os']['writeAcceleratorEnabled']
if os_disk_delete_option is not None:
profile['osDisk']['deleteOption'] = os_disk_delete_option
if os_disk_storage_fault_domain_alignment is not None:
profile['osDisk']['storageFaultDomainAlignment'] = os_disk_storage_fault_domain_alignment
Comment on lines +565 to +566
data_disks = [v for k, v in disk_info.items() if k != 'os']
if data_disk_encryption_sets:
if len(data_disk_encryption_sets) != len(data_disks):
Expand All @@ -575,6 +578,8 @@ def _build_storage_profile():
data_disk['diskIOPSReadWrite'] = disk_iops_read_write
if disk_mbps_read_write is not None:
data_disk['diskMBPSReadWrite'] = disk_mbps_read_write
if data_disk_storage_fault_domain_alignment is not None:
data_disk['storageFaultDomainAlignment'] = data_disk_storage_fault_domain_alignment
if disk_info['os'].get('diffDiskSettings'):
profile['osDisk']['diffDiskSettings'] = disk_info['os']['diffDiskSettings']

Expand Down Expand Up @@ -1077,7 +1082,8 @@ def build_vmss_resource(cmd, name, computer_name_prefix, location, tags, overpro
automatic_zone_balancing_strategy=None, automatic_zone_balancing_behavior=None,
enable_automatic_repairs=None, zone_placement_policy=None, include_zones=None,
exclude_zones=None, max_zone_count=None, instance_percent_policy=None,
max_instance_percent=None):
max_instance_percent=None, data_disk_storage_fault_domain_alignment=None,
os_disk_storage_fault_domain_alignment=None, zonal_platform_fault_domain_align_mode=None):

# Build IP configuration
ip_configuration = {}
Expand Down Expand Up @@ -1271,6 +1277,13 @@ def build_vmss_resource(cmd, name, computer_name_prefix, location, tags, overpro
if disk_controller_type is not None:
storage_properties['diskControllerType'] = disk_controller_type

# Aligned Zonal Fault Domains: per-disk storage fault domain alignment overrides.
if os_disk_storage_fault_domain_alignment is not None and 'osDisk' in storage_properties:
storage_properties['osDisk']['storageFaultDomainAlignment'] = os_disk_storage_fault_domain_alignment
if data_disk_storage_fault_domain_alignment is not None and storage_properties.get('dataDisks'):
for data_disk in storage_properties['dataDisks']:
data_disk['storageFaultDomainAlignment'] = data_disk_storage_fault_domain_alignment

# Build OS Profile
os_profile = {}
if computer_name_prefix:
Expand Down Expand Up @@ -1500,6 +1513,9 @@ def build_vmss_resource(cmd, name, computer_name_prefix, location, tags, overpro
min_api='2017-12-01', operation_group='virtual_machine_scale_sets'):
vmss_properties['platformFaultDomainCount'] = platform_fault_domain_count

if zonal_platform_fault_domain_align_mode is not None:
vmss_properties['zonalPlatformFaultDomainAlignMode'] = zonal_platform_fault_domain_align_mode

if ultra_ssd_enabled is not None:
if cmd.supported_api_version(min_api='2019-03-01', operation_group='virtual_machine_scale_sets'):
vmss_properties['additionalCapabilities'] = {'ultraSSDEnabled': ultra_ssd_enabled}
Expand Down
56 changes: 51 additions & 5 deletions src/azure-cli/azure/cli/command_modules/vm/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,37 @@ def _validate_vm_create_availability_set(cmd, namespace):
logger.debug("adding to specified availability set '%s'", namespace.availability_set)


def _validate_vm_create_disk_alignment(cmd, namespace):
from .operations.vmss import VMSSShow
from azure.mgmt.core.tools import parse_resource_id

if namespace.data_disk_storage_fault_domain_alignment is None \
and namespace.os_disk_storage_fault_domain_alignment is None:
return

if not namespace.vmss:
raise ArgumentUsageError('usage error: --data-disk-storage-fault-domain-alignment/ '
'--os-disk-storage-fault-domain-alignment '
'is only available for VM in a Flex VMSS.')

Comment on lines +772 to +776
vmss_show = VMSSShow(cmd.cli_ctx)(command_args={
'resource_group': namespace.resource_group_name,
'vm_scale_set_name': parse_resource_id(namespace.vmss)['name']
})

flexible_str = 'Flexible'

if vmss_show.get('orchestrationMode') != flexible_str:
raise ArgumentUsageError('usage error: --data-disk-storage-fault-domain-alignment/ '
'--os-disk-storage-fault-domain-alignment '
'is only available for VM in a Flex VMSS.')

if len(vmss_show.get('zones', [])) != 1:
raise ArgumentUsageError('usage error: --data-disk-storage-fault-domain-alignment/ '
'--os-disk-storage-fault-domain-alignment '
'is only available for VM in a single Availability Zone VMSS.')


def _validate_vm_create_vmss(cmd, namespace):
from azure.mgmt.core.tools import parse_resource_id, resource_id
from azure.cli.core.commands.client_factory import get_subscription_id
Expand Down Expand Up @@ -1610,6 +1641,7 @@ def process_vm_create_namespace(cmd, namespace):
_validate_vm_create_storage_account(cmd, namespace)

_validate_vm_create_availability_set(cmd, namespace)
_validate_vm_create_disk_alignment(cmd, namespace)
_validate_vm_create_vmss(cmd, namespace)
_validate_vm_vmss_create_vnet(cmd, namespace)
_validate_vm_create_nsg(cmd, namespace)
Expand Down Expand Up @@ -1802,13 +1834,27 @@ def process_vmss_create_namespace(cmd, namespace):

if namespace.os_disk_delete_option is not None or namespace.data_disk_delete_option is not None:
if namespace.orchestration_mode.lower() != flexible_str.lower():
raise InvalidArgumentValueError('usage error: --os-disk-delete-option/--data-disk-delete-option is only'
' available for VMSS with flexible orchestration mode')
raise InvalidArgumentValueError('usage error: --os-disk-delete-option/--data-disk-delete-option is only '
'available for VMSS with flexible orchestration mode')

if namespace.regular_priority_count is not None or namespace.regular_priority_percentage is not None:
if namespace.orchestration_mode.lower() != flexible_str.lower():
raise InvalidArgumentValueError('usage error: --regular-priority-count/--regular-priority-percentage is'
' only available for VMSS with flexible orchestration mode')
raise InvalidArgumentValueError('usage error: --regular-priority-count/--regular-priority-percentage is '
'only available for VMSS with flexible orchestration mode')

if namespace.data_disk_storage_fault_domain_alignment is not None \
or namespace.os_disk_storage_fault_domain_alignment is not None \
or namespace.zonal_platform_fault_domain_align_mode is not None:
if namespace.orchestration_mode.lower() != flexible_str.lower():
raise ArgumentUsageError('usage error: --data-disk-storage-fault-domain-alignment/ '
'--os-disk-storage-fault-domain-alignment/ '
'--zonal-platform-fault-domain-align-mode '
'is only available for VMSS with flexible orchestration mode')
if not namespace.zones and len(namespace.zones) != 1:
raise ArgumentUsageError('usage error: --data-disk-storage-fault-domain-alignment/ '
'--os-disk-storage-fault-domain-alignment/ '
'--zonal-platform-fault-domain-align-mode '
'is only available for VMSS with single Availability Zone')

if namespace.orchestration_mode.lower() == flexible_str.lower():

Expand Down Expand Up @@ -1887,7 +1933,7 @@ def process_vmss_create_namespace(cmd, namespace):
namespace.authentication_type, namespace.os_type]):
_validate_vm_vmss_create_auth(namespace, cmd)
if namespace.assign_identity == '[system]':
raise InvalidArgumentValueError('usage error: only user assigned indetity is suppoprted for Flex mode.')
raise InvalidArgumentValueError('usage error: only user assigned identity is supported for Flex mode.')
if namespace.assign_identity is not None:
_validate_vm_vmss_msi(cmd, namespace) # -- UserAssignedOnly
_validate_proximity_placement_group(cmd, namespace)
Expand Down
11 changes: 11 additions & 0 deletions src/azure-cli/azure/cli/command_modules/vm/_vm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -842,3 +842,14 @@ class OrchestrationServiceNames(Enum):
class OrchestrationServiceStateAction(Enum):
RESUME = 'Resume'
SUSPEND = 'Suspend'


class DiskStorageAlignment(Enum):
ALIGNED = 'Aligned'
BEST_EFFORT_ALIGNED = 'BestEffortAligned'


class FaultDomainAlignment(Enum):
ALIGNED = 'Aligned'
BEST_EFFORT_ALIGNED = 'BestEffortAligned'
UNALIGNED = 'Unaligned'
15 changes: 11 additions & 4 deletions src/azure-cli/azure/cli/command_modules/vm/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,8 @@ def create_vm(cmd, vm_name, resource_group_name, image=None, size='Standard_D2s_
exclude_zones=None, align_regional_disks_to_vm_zone=None, wire_server_mode=None, imds_mode=None,
wire_server_access_control_profile_reference_id=None, imds_access_control_profile_reference_id=None,
key_incarnation_id=None, add_proxy_agent_extension=None, disk_iops_read_write=None,
disk_mbps_read_write=None, zone_movement=None):
disk_mbps_read_write=None, zone_movement=None,
os_disk_storage_fault_domain_alignment=None, data_disk_storage_fault_domain_alignment=None):

from azure.cli.core.util import random_string, hash_string
from azure.cli.core.commands.arm import ArmTemplateBuilder
Expand Down Expand Up @@ -1159,7 +1160,8 @@ def create_vm(cmd, vm_name, resource_group_name, image=None, size='Standard_D2s_
imds_access_control_profile_reference_id=imds_access_control_profile_reference_id,
key_incarnation_id=key_incarnation_id, add_proxy_agent_extension=add_proxy_agent_extension,
disk_iops_read_write=disk_iops_read_write, disk_mbps_read_write=disk_mbps_read_write,
zone_movement=zone_movement)
zone_movement=zone_movement, os_disk_storage_fault_domain_alignment=os_disk_storage_fault_domain_alignment,
data_disk_storage_fault_domain_alignment=data_disk_storage_fault_domain_alignment)

vm_resource['dependsOn'] = vm_dependencies

Expand Down Expand Up @@ -3736,7 +3738,9 @@ def create_vmss(cmd, vmss_name, resource_group_name, image=None,
imds_access_control_profile_reference_id=None, enable_automatic_zone_balancing=None,
automatic_zone_balancing_strategy=None, automatic_zone_balancing_behavior=None,
enable_automatic_repairs=None, zone_placement_policy=None, include_zones=None,
exclude_zones=None, max_zone_count=None, instance_percent_policy=None, max_instance_percent=None):
exclude_zones=None, max_zone_count=None, instance_percent_policy=None, max_instance_percent=None,
data_disk_storage_fault_domain_alignment=None, os_disk_storage_fault_domain_alignment=None,
zonal_platform_fault_domain_align_mode=None):
from azure.cli.core.util import random_string, hash_string
from azure.cli.core.commands.arm import ArmTemplateBuilder
from azure.cli.command_modules.vm._template_builder import (StorageProfile, build_vmss_resource,
Expand Down Expand Up @@ -4063,7 +4067,10 @@ def _get_public_ip_address_allocation(value, sku):
automatic_zone_balancing_behavior=automatic_zone_balancing_behavior,
enable_automatic_repairs=enable_automatic_repairs, zone_placement_policy=zone_placement_policy,
include_zones=include_zones, exclude_zones=exclude_zones, max_zone_count=max_zone_count,
instance_percent_policy=instance_percent_policy, max_instance_percent=max_instance_percent)
instance_percent_policy=instance_percent_policy, max_instance_percent=max_instance_percent,
data_disk_storage_fault_domain_alignment=data_disk_storage_fault_domain_alignment,
os_disk_storage_fault_domain_alignment=os_disk_storage_fault_domain_alignment,
zonal_platform_fault_domain_align_mode=zonal_platform_fault_domain_align_mode)

vmss_resource['dependsOn'] = vmss_dependencies

Expand Down
Loading
Loading