Skip to content
Open
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7700,6 +7700,10 @@ def test_network_watcher_packet_capture(self, resource_group, resource_group_loc
self.cmd('network watcher packet-capture delete -l {loc} -n {capture2}')
self.cmd('network watcher packet-capture list -l {loc}')

@unittest.skip(
"Skip due to NetworkWatcherInvalidTargetReference on VMSS target in live tests "
"(Network Watcher backend eventually-consistent / subscription & region dependent)"
)
@mock.patch('azure.cli.command_modules.vm._actions._get_thread_count', _mock_thread_count)
@ResourceGroupPreparer(name_prefix='cli_test_nw_packet_capture_vmss_as_target', location='westcentralus')
@AllowLargeResponse()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
from ._update import *
from ._update_domain_walk import *
from ._wait import *
from ._extension_rolling_upgrade import *
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
#
# Code generated by aaz-dev-tools
# --------------------------------------------------------------------------------------------

# pylint: skip-file
# flake8: noqa

from azure.cli.core.aaz import *


class ExtensionRollingUpgrade(AAZCommand):
"""Starts a rolling upgrade to move all extensions for all virtual machine scale set instances to the latest available extension version.
Instances which are already running the latest extension versions are not affected.

:example: Upgrade a vmss extension
az vmss extension-rolling-upgrade -g MyResourceGroup --ids id1 --vm-scale-set-name MyVmssName
"""

_aaz_info = {
"version": "2024-11-01",
"resources": [
["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.compute/virtualmachinescalesets/{}/extensionrollingupgrade", "2024-11-01"],
]
}

AZ_SUPPORT_NO_WAIT = True

def _handler(self, command_args):
super()._handler(command_args)
return self.build_lro_poller(self._execute_operations, None)

_args_schema = None

@classmethod
def _build_arguments_schema(cls, *args, **kwargs):
if cls._args_schema is not None:
return cls._args_schema
cls._args_schema = super()._build_arguments_schema(*args, **kwargs)

# define Arg Group ""

_args_schema = cls._args_schema
_args_schema.resource_group = AAZResourceGroupNameArg(
required=True,
)
_args_schema.vm_scale_set_name = AAZStrArg(
options=["--vm-scale-set-name"],
help="The name of the VM scale set.",
required=True,
id_part="name",
)
return cls._args_schema

def _execute_operations(self):
self.pre_operations()
yield self.VirtualMachineScaleSetRollingUpgradesStartExtensionUpgrade(ctx=self.ctx)()
self.post_operations()

@register_callback
def pre_operations(self):
pass

@register_callback
def post_operations(self):
pass

class VirtualMachineScaleSetRollingUpgradesStartExtensionUpgrade(AAZHttpOperation):
CLIENT_TYPE = "MgmtClient"

def __call__(self, *args, **kwargs):
request = self.make_request()
session = self.client.send_request(request=request, stream=False, **kwargs)
if session.http_response.status_code in [202]:
return self.client.build_lro_polling(
self.ctx.args.no_wait,
session,
self.on_200,
self.on_error,
lro_options={"final-state-via": "location"},
path_format_arguments=self.url_parameters,
)
if session.http_response.status_code in [200]:
return self.client.build_lro_polling(
self.ctx.args.no_wait,
session,
self.on_200,
self.on_error,
lro_options={"final-state-via": "location"},
path_format_arguments=self.url_parameters,
)

return self.on_error(session.http_response)

@property
def url(self):
return self.client.format_url(
"/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Compute/virtualMachineScaleSets/{vmScaleSetName}/extensionRollingUpgrade",
**self.url_parameters
)

@property
def method(self):
return "POST"

@property
def error_format(self):
return "ODataV4Format"

@property
def url_parameters(self):
parameters = {
**self.serialize_url_param(
"resourceGroupName", self.ctx.args.resource_group,
required=True,
),
**self.serialize_url_param(
"subscriptionId", self.ctx.subscription_id,
required=True,
),
**self.serialize_url_param(
"vmScaleSetName", self.ctx.args.vm_scale_set_name,
required=True,
),
}
return parameters

@property
def query_parameters(self):
parameters = {
**self.serialize_query_param(
"api-version", "2024-11-01",
required=True,
),
}
return parameters

def on_200(self, session):
pass


class _ExtensionRollingUpgradeHelper:
"""Helper class for ExtensionRollingUpgrade"""


__all__ = ["ExtensionRollingUpgrade"]
151 changes: 89 additions & 62 deletions src/azure-cli/azure/cli/command_modules/vm/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -5104,43 +5104,52 @@ def detach_disk_from_vmss(cmd, resource_group_name, vmss_name, lun, instance_id=

# region VirtualMachineScaleSets Extensions
def delete_vmss_extension(cmd, resource_group_name, vmss_name, extension_name):
client = _compute_client_factory(cmd.cli_ctx)
vmss = client.virtual_machine_scale_sets.get(resource_group_name=resource_group_name, vm_scale_set_name=vmss_name)
from .operations.vmss import VMSSShow
vmss = VMSSShow(cli_ctx=cmd.cli_ctx)(command_args={
"resource_group": resource_group_name,
"vm_scale_set_name": vmss_name,
})
# Avoid unnecessary permission error
vmss.virtual_machine_profile.storage_profile.image_reference = None
# pylint: disable=no-member
if not vmss.virtual_machine_profile.extension_profile:
vmss["virtualMachineProfile"]["storageProfile"]["imageReference"] = None

if not vmss.get("virtualMachineProfile", {}).get("extensionProfile", {}).get("extensions", []):
raise CLIError('Scale set has no extensions to delete')

keep_list = [e for e in vmss.virtual_machine_profile.extension_profile.extensions
if e.name != extension_name]
if len(keep_list) == len(vmss.virtual_machine_profile.extension_profile.extensions):
keep_list = [e for e in vmss.get("virtualMachineProfile", {}).get("extensionProfile", {}).get("extensions", [])
if e["name"] != extension_name]
if len(keep_list) == len(vmss.get("virtualMachineProfile", {}).get("extensionProfile", {}).get("extensions", [])):
raise CLIError('Extension {} not found'.format(extension_name))

vmss.virtual_machine_profile.extension_profile.extensions = keep_list
vmss["virtualMachineProfile"]["extensionProfile"]["extensions"] = keep_list
vmss["resource_group"] = resource_group_name
vmss["vm_scale_set_name"] = vmss_name

return client.virtual_machine_scale_sets.begin_create_or_update(resource_group_name=resource_group_name,
vm_scale_set_name=vmss_name, parameters=vmss)
from .operations.vmss import VMSSCreate
return VMSSCreate(cli_ctx=cmd.cli_ctx)(command_args=vmss)


# pylint: disable=inconsistent-return-statements
def get_vmss_extension(cmd, resource_group_name, vmss_name, extension_name):
client = _compute_client_factory(cmd.cli_ctx)
vmss = client.virtual_machine_scale_sets.get(resource_group_name=resource_group_name, vm_scale_set_name=vmss_name)
# pylint: disable=no-member
if not vmss.virtual_machine_profile.extension_profile:
from .operations.vmss import VMSSShow
vmss = VMSSShow(cli_ctx=cmd.cli_ctx)(command_args={
"resource_group": resource_group_name,
"vm_scale_set_name": vmss_name,
})

if not vmss.get("virtualMachineProfile", {}).get("extensionProfile", {}).get("extensions", []):
return
return next((e for e in vmss.virtual_machine_profile.extension_profile.extensions
if e.name == extension_name), None)
return next((e for e in vmss.get("virtualMachineProfile", {}).get("extensionProfile", {}).get("extensions", [])
if e["name"] == extension_name), None)


def list_vmss_extensions(cmd, resource_group_name, vmss_name):
client = _compute_client_factory(cmd.cli_ctx)
vmss = client.virtual_machine_scale_sets.get(resource_group_name=resource_group_name, vm_scale_set_name=vmss_name)
# pylint: disable=no-member
if vmss.virtual_machine_profile and vmss.virtual_machine_profile.extension_profile:
return vmss.virtual_machine_profile.extension_profile.extensions
return None
from .operations.vmss import VMSSShow
vmss = VMSSShow(cli_ctx=cmd.cli_ctx)(command_args={
"resource_group": resource_group_name,
"vm_scale_set_name": vmss_name,
})

return vmss.get("virtualMachineProfile", {}).get("extensionProfile", {}).get("extensions", None)


def set_vmss_extension(cmd, resource_group_name, vmss_name, extension_name, publisher, version=None,
Expand All @@ -5154,52 +5163,66 @@ def set_vmss_extension(cmd, resource_group_name, vmss_name, extension_name, publ
if extension_name in auto_upgrade_extensions and enable_auto_upgrade is None:
enable_auto_upgrade = True

client = _compute_client_factory(cmd.cli_ctx)
vmss = client.virtual_machine_scale_sets.get(resource_group_name=resource_group_name, vm_scale_set_name=vmss_name)
from .operations.vmss import VMSSShow
vmss = VMSSShow(cli_ctx=cmd.cli_ctx)(command_args={
"resource_group": resource_group_name,
"vm_scale_set_name": vmss_name,
})
# Avoid unnecessary permission error
vmss.virtual_machine_profile.storage_profile.image_reference = None
VirtualMachineScaleSetExtension, VirtualMachineScaleSetExtensionProfile = cmd.get_models(
'VirtualMachineScaleSetExtension', 'VirtualMachineScaleSetExtensionProfile')
vmss["virtualMachineProfile"]["storageProfile"]["imageReference"] = None

# pylint: disable=no-member
version = _normalize_extension_version(cmd.cli_ctx, publisher, extension_name, version, vmss.location)
extension_profile = vmss.virtual_machine_profile.extension_profile
version = _normalize_extension_version(cmd.cli_ctx, publisher, extension_name, version, vmss.get("location"))
extension_profile = vmss.get("virtualMachineProfile", {}).get("extensionProfile", {})
if extension_profile:
extensions = extension_profile.extensions
extensions = extension_profile.get("extensions", [])
if extensions:
extension_profile.extensions = [x for x in extensions if
x.type_properties_type.lower() != extension_name.lower() or x.publisher.lower() != publisher.lower()] # pylint: disable=line-too-long

if cmd.supported_api_version(min_api='2019-07-01', operation_group='virtual_machine_scale_sets'):
ext = VirtualMachineScaleSetExtension(name=extension_instance_name,
publisher=publisher,
type_properties_type=extension_name,
protected_settings=protected_settings,
type_handler_version=version,
settings=settings,
auto_upgrade_minor_version=(not no_auto_upgrade),
provision_after_extensions=provision_after_extensions,
enable_automatic_upgrade=enable_auto_upgrade)
extension_profile["extensions"] = [x for x in extensions if
x["name"].lower() != extension_name.lower() or x["publisher"].lower() != publisher.lower()] # pylint: disable=line-too-long

if cmd.supported_api_version(min_api='2025-04-01', operation_group='virtual_machine_scale_sets'):
ext = {
"autoUpgradeMinorVersion": not no_auto_upgrade,
"enableAutomaticUpgrade": enable_auto_upgrade,
"name": extension_instance_name,
"publisher": publisher,
"typePropertiesType": extension_name,
"typeHandlerVersion": version,
"settings": settings,
"protectedSettings": protected_settings,
"provisionAfterExtensions": provision_after_extensions,
}
else:
ext = VirtualMachineScaleSetExtension(name=extension_instance_name,
publisher=publisher,
type=extension_name,
protected_settings=protected_settings,
type_handler_version=version,
settings=settings,
auto_upgrade_minor_version=(not no_auto_upgrade),
provision_after_extensions=provision_after_extensions,
enable_automatic_upgrade=enable_auto_upgrade)
ext = {
"autoUpgradeMinorVersion": not no_auto_upgrade,
"enableAutomaticUpgrade": enable_auto_upgrade,
"name": extension_instance_name,
"publisher": publisher,
"type": extension_name,
"typeHandlerVersion": version,
"settings": settings,
"protectedSettings": protected_settings,
"provisionAfterExtensions": provision_after_extensions,
}

if force_update:
ext.force_update_tag = str(_gen_guid())
ext["forceUpdateTag"] = str(_gen_guid())

if not vmss.virtual_machine_profile.extension_profile:
vmss.virtual_machine_profile.extension_profile = VirtualMachineScaleSetExtensionProfile(extensions=[])
vmss.virtual_machine_profile.extension_profile.extensions.append(ext)
if no_wait:
vmss["no_wait"] = no_wait

return sdk_no_wait(no_wait, client.virtual_machine_scale_sets.begin_create_or_update,
resource_group_name, vmss_name, vmss)
if not vmss.get("virtualMachineProfile", {}).get("extensionProfile", {}).get("extensions", []):
vmss.setdefault("virtualMachineProfile", {}) \
.setdefault("extensionProfile", {}) \
.setdefault("extensions", [])

vmss["virtualMachineProfile"]["extensionProfile"]["extensions"].append(ext)

vmss["resource_group"] = resource_group_name
vmss["vm_scale_set_name"] = vmss_name

from .operations.vmss import VMSSCreate
return VMSSCreate(cli_ctx=cmd.cli_ctx)(command_args=vmss)


def set_orchestration_service_state(cmd, resource_group_name, vm_scale_set_name, service_name, action, no_wait=False):
Expand All @@ -5214,9 +5237,13 @@ def set_orchestration_service_state(cmd, resource_group_name, vm_scale_set_name,


def upgrade_vmss_extension(cmd, resource_group_name, vm_scale_set_name, no_wait=False):
client = _compute_client_factory(cmd.cli_ctx)
return sdk_no_wait(no_wait, client.virtual_machine_scale_set_rolling_upgrades.begin_start_extension_upgrade,
resource_group_name, vm_scale_set_name)
from azure.cli.command_modules.vm.aaz.latest.vmss import ExtensionRollingUpgrade

return ExtensionRollingUpgrade(cli_ctx=cmd.cli_ctx)(command_args={
"resource_group": resource_group_name,
"vm_scale_set_name": vm_scale_set_name,
"no_wait": no_wait,
})
# endregion


Expand Down
Loading