From 4d11e86225339e59f6db7982318e08768b377561 Mon Sep 17 00:00:00 2001 From: Jordan Graves Date: Tue, 21 Jul 2026 23:54:08 -0700 Subject: [PATCH] Added in Migration for NVA --- src/azure-cli/HISTORY.rst | 1 + .../migration/__cmd_group.py | 26 +++ .../virtual_appliance/migration/__init__.py | 15 ++ .../virtual_appliance/migration/_abort.py | 164 +++++++++++++++ .../virtual_appliance/migration/_commit.py | 192 +++++++++++++++++ .../virtual_appliance/migration/_execute.py | 192 +++++++++++++++++ .../virtual_appliance/migration/_prepare.py | 198 ++++++++++++++++++ .../tests/latest/test_network_commands.py | 92 ++++++++ 8 files changed, 880 insertions(+) create mode 100644 src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/__cmd_group.py create mode 100644 src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/__init__.py create mode 100644 src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_abort.py create mode 100644 src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_commit.py create mode 100644 src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_execute.py create mode 100644 src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_prepare.py diff --git a/src/azure-cli/HISTORY.rst b/src/azure-cli/HISTORY.rst index 7bbafa33310..bfa8435e12b 100644 --- a/src/azure-cli/HISTORY.rst +++ b/src/azure-cli/HISTORY.rst @@ -82,6 +82,7 @@ Release History * `az network ddos-custom-policy`: Support specifying frontend IP configuration associations (#33413) * `az network traffic-manager profile create/update`: Add `--record-type` parameter to support record type filtering (#33503) * `az network private-endpoint-connection`: Add provider `Microsoft.HorizonDB/clusters` (#33644) +* `az network virtual-appliance migration prepare/execute/commit/abort`: Add commands to migrate a Network Virtual Appliance to the new internal load balancer (ILB) architecture **Packaging** diff --git a/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/__cmd_group.py b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/__cmd_group.py new file mode 100644 index 00000000000..d451fc2d60f --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/__cmd_group.py @@ -0,0 +1,26 @@ +# -------------------------------------------------------------------------------------------- +# 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 * + + +@register_command_group( + "network virtual-appliance migration", +) +class __CMDGroup(AAZCommandGroup): + """Manage the infrastructure migration of an Azure Network Virtual Appliance. + + The migration workflow is executed in phases. Run `prepare` first, then `execute`, and finally + `commit` to complete the migration or `abort` to roll it back. + """ + pass + + +__all__ = ["__CMDGroup"] diff --git a/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/__init__.py b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/__init__.py new file mode 100644 index 00000000000..d63e1abee3b --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/__init__.py @@ -0,0 +1,15 @@ +# -------------------------------------------------------------------------------------------- +# 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 .__cmd_group import * +from ._abort import * +from ._commit import * +from ._execute import * +from ._prepare import * diff --git a/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_abort.py b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_abort.py new file mode 100644 index 00000000000..ae7fa787417 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_abort.py @@ -0,0 +1,164 @@ +# -------------------------------------------------------------------------------------------- +# 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 * + + +@register_command( + "network virtual-appliance migration abort", + confirmation="Are you sure you want to abort the in-progress migration for this Network Virtual Appliance?", +) +class Abort(AAZCommand): + """Abort an in-progress migration of a Network Virtual Appliance. + + Aborting a migration discards any changes staged by the `prepare` or `execute` phases and restores + the Network Virtual Appliance to its previous configuration. The migration type is inferred from the + in-progress migration on the resource. This action cannot be run once the migration has been committed. + + :example: Abort an in-progress Network Virtual Appliance migration. + az network virtual-appliance migration abort -n MyName -g MyRG + """ + + _aaz_info = { + "version": "2025-09-01", + "resources": [ + ["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.network/networkvirtualappliances/{}/abortmigration", "2025-09-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.network_virtual_appliance_name = AAZStrArg( + options=["-n", "--name", "--network-virtual-appliance-name"], + help="The name of the Network Virtual Appliance.", + required=True, + id_part="name", + ) + _args_schema.resource_group = AAZResourceGroupNameArg( + required=True, + ) + return cls._args_schema + + def _execute_operations(self): + self.pre_operations() + yield self.NetworkVirtualAppliancesAbortMigration(ctx=self.ctx)() + self.post_operations() + + @register_callback + def pre_operations(self): + pass + + @register_callback + def post_operations(self): + pass + + class NetworkVirtualAppliancesAbortMigration(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.Network/networkVirtualAppliances/{networkVirtualApplianceName}/abortMigration", + **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( + "networkVirtualApplianceName", self.ctx.args.network_virtual_appliance_name, + required=True, + ), + **self.serialize_url_param( + "resourceGroupName", self.ctx.args.resource_group, + required=True, + ), + **self.serialize_url_param( + "subscriptionId", self.ctx.subscription_id, + required=True, + ), + } + return parameters + + @property + def query_parameters(self): + parameters = { + **self.serialize_query_param( + "api-version", "2025-09-01", + required=True, + ), + } + return parameters + + @property + def header_parameters(self): + parameters = { + **self.serialize_header_param( + "Accept", "application/json", + ), + } + return parameters + + def on_200(self, session): + pass + + +class _AbortHelper: + """Helper class for Abort""" + + +__all__ = ["Abort"] diff --git a/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_commit.py b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_commit.py new file mode 100644 index 00000000000..330cbfecf8b --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_commit.py @@ -0,0 +1,192 @@ +# -------------------------------------------------------------------------------------------- +# 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 * + + +@register_command( + "network virtual-appliance migration commit", +) +class Commit(AAZCommand): + """Commit the migration of a Network Virtual Appliance. + + This is the final phase of the migration workflow and must be run after `execute`. Committing a + migration finalizes the new infrastructure and discards the previous configuration. This action + cannot be reversed with `abort`. + + :example: Commit the migration of a Network Virtual Appliance to the new internal load balancer (ILB) architecture. + az network virtual-appliance migration commit -n MyName -g MyRG --migration-type MigrateToNewILBArchitecture + """ + + _aaz_info = { + "version": "2025-09-01", + "resources": [ + ["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.network/networkvirtualappliances/{}/commitmigration", "2025-09-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.network_virtual_appliance_name = AAZStrArg( + options=["-n", "--name", "--network-virtual-appliance-name"], + help="The name of the Network Virtual Appliance.", + required=True, + id_part="name", + ) + _args_schema.resource_group = AAZResourceGroupNameArg( + required=True, + ) + + # define Arg Group "Properties" + + _args_schema = cls._args_schema + _args_schema.migration_type = AAZStrArg( + options=["--migration-type"], + arg_group="Properties", + help="The type of migration workflow to commit for the Network Virtual Appliance. Must match the type used in the prepare and execute phases.", + required=True, + enum={"MigrateToNewILBArchitecture": "MigrateToNewILBArchitecture", "MigrateToNewOSVersion": "MigrateToNewOSVersion"}, + ) + return cls._args_schema + + def _execute_operations(self): + self.pre_operations() + yield self.NetworkVirtualAppliancesCommitMigration(ctx=self.ctx)() + self.post_operations() + + @register_callback + def pre_operations(self): + pass + + @register_callback + def post_operations(self): + pass + + class NetworkVirtualAppliancesCommitMigration(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.Network/networkVirtualAppliances/{networkVirtualApplianceName}/commitMigration", + **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( + "networkVirtualApplianceName", self.ctx.args.network_virtual_appliance_name, + required=True, + ), + **self.serialize_url_param( + "resourceGroupName", self.ctx.args.resource_group, + required=True, + ), + **self.serialize_url_param( + "subscriptionId", self.ctx.subscription_id, + required=True, + ), + } + return parameters + + @property + def query_parameters(self): + parameters = { + **self.serialize_query_param( + "api-version", "2025-09-01", + required=True, + ), + } + return parameters + + @property + def header_parameters(self): + parameters = { + **self.serialize_header_param( + "Content-Type", "application/json", + ), + **self.serialize_header_param( + "Accept", "application/json", + ), + } + return parameters + + @property + def content(self): + _content_value, _builder = self.new_content_builder( + self.ctx.args, + typ=AAZObjectType, + typ_kwargs={"flags": {"client_flatten": True}} + ) + _builder.set_prop("properties", AAZObjectType, typ_kwargs={"flags": {"client_flatten": True}}) + + properties = _builder.get(".properties") + if properties is not None: + properties.set_prop("migrationType", AAZStrType, ".migration_type", typ_kwargs={"flags": {"required": True}}) + + return self.serialize_content(_content_value) + + def on_200(self, session): + pass + + +class _CommitHelper: + """Helper class for Commit""" + + +__all__ = ["Commit"] diff --git a/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_execute.py b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_execute.py new file mode 100644 index 00000000000..a08ec1583fa --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_execute.py @@ -0,0 +1,192 @@ +# -------------------------------------------------------------------------------------------- +# 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 * + + +@register_command( + "network virtual-appliance migration execute", +) +class Execute(AAZCommand): + """Execute the migration of a Network Virtual Appliance. + + This is the second phase of the migration workflow and must be run after `prepare`. Executing a + migration applies the new infrastructure to the Network Virtual Appliance. Once the execution + completes, run `commit` to finalize the migration or `abort` to roll it back. + + :example: Execute the migration of a Network Virtual Appliance to the new internal load balancer (ILB) architecture. + az network virtual-appliance migration execute -n MyName -g MyRG --migration-type MigrateToNewILBArchitecture + """ + + _aaz_info = { + "version": "2025-09-01", + "resources": [ + ["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.network/networkvirtualappliances/{}/executemigration", "2025-09-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.network_virtual_appliance_name = AAZStrArg( + options=["-n", "--name", "--network-virtual-appliance-name"], + help="The name of the Network Virtual Appliance.", + required=True, + id_part="name", + ) + _args_schema.resource_group = AAZResourceGroupNameArg( + required=True, + ) + + # define Arg Group "Properties" + + _args_schema = cls._args_schema + _args_schema.migration_type = AAZStrArg( + options=["--migration-type"], + arg_group="Properties", + help="The type of migration workflow to execute for the Network Virtual Appliance. Must match the type used in the prepare phase.", + required=True, + enum={"MigrateToNewILBArchitecture": "MigrateToNewILBArchitecture", "MigrateToNewOSVersion": "MigrateToNewOSVersion"}, + ) + return cls._args_schema + + def _execute_operations(self): + self.pre_operations() + yield self.NetworkVirtualAppliancesExecuteMigration(ctx=self.ctx)() + self.post_operations() + + @register_callback + def pre_operations(self): + pass + + @register_callback + def post_operations(self): + pass + + class NetworkVirtualAppliancesExecuteMigration(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.Network/networkVirtualAppliances/{networkVirtualApplianceName}/executeMigration", + **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( + "networkVirtualApplianceName", self.ctx.args.network_virtual_appliance_name, + required=True, + ), + **self.serialize_url_param( + "resourceGroupName", self.ctx.args.resource_group, + required=True, + ), + **self.serialize_url_param( + "subscriptionId", self.ctx.subscription_id, + required=True, + ), + } + return parameters + + @property + def query_parameters(self): + parameters = { + **self.serialize_query_param( + "api-version", "2025-09-01", + required=True, + ), + } + return parameters + + @property + def header_parameters(self): + parameters = { + **self.serialize_header_param( + "Content-Type", "application/json", + ), + **self.serialize_header_param( + "Accept", "application/json", + ), + } + return parameters + + @property + def content(self): + _content_value, _builder = self.new_content_builder( + self.ctx.args, + typ=AAZObjectType, + typ_kwargs={"flags": {"client_flatten": True}} + ) + _builder.set_prop("properties", AAZObjectType, typ_kwargs={"flags": {"client_flatten": True}}) + + properties = _builder.get(".properties") + if properties is not None: + properties.set_prop("migrationType", AAZStrType, ".migration_type", typ_kwargs={"flags": {"required": True}}) + + return self.serialize_content(_content_value) + + def on_200(self, session): + pass + + +class _ExecuteHelper: + """Helper class for Execute""" + + +__all__ = ["Execute"] diff --git a/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_prepare.py b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_prepare.py new file mode 100644 index 00000000000..f3ce9dde28f --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/network/aaz/latest/network/virtual_appliance/migration/_prepare.py @@ -0,0 +1,198 @@ +# -------------------------------------------------------------------------------------------- +# 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 * + + +@register_command( + "network virtual-appliance migration prepare", +) +class Prepare(AAZCommand): + """Prepare a Network Virtual Appliance for an infrastructure migration. + + This is the first phase of the migration workflow and must be run before `execute`. Preparing a + migration validates that the Network Virtual Appliance is eligible for the requested migration and + stages the resources required to perform it. + + :example: Prepare a Network Virtual Appliance for migration to the new internal load balancer (ILB) architecture. + az network virtual-appliance migration prepare -n MyName -g MyRG --migration-type MigrateToNewILBArchitecture + """ + + _aaz_info = { + "version": "2025-09-01", + "resources": [ + ["mgmt-plane", "/subscriptions/{}/resourcegroups/{}/providers/microsoft.network/networkvirtualappliances/{}/preparemigration", "2025-09-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.network_virtual_appliance_name = AAZStrArg( + options=["-n", "--name", "--network-virtual-appliance-name"], + help="The name of the Network Virtual Appliance.", + required=True, + id_part="name", + ) + _args_schema.resource_group = AAZResourceGroupNameArg( + required=True, + ) + + # define Arg Group "Properties" + + _args_schema = cls._args_schema + _args_schema.migration_type = AAZStrArg( + options=["--migration-type"], + arg_group="Properties", + help="The type of migration workflow to prepare for the Network Virtual Appliance.", + required=True, + enum={"MigrateToNewILBArchitecture": "MigrateToNewILBArchitecture", "MigrateToNewOSVersion": "MigrateToNewOSVersion"}, + ) + _args_schema.marketplace_version = AAZStrArg( + options=["--marketplace-version"], + arg_group="Properties", + help="The marketplace version to migrate to. Only applicable when --migration-type is MigrateToNewOSVersion.", + ) + return cls._args_schema + + def _execute_operations(self): + self.pre_operations() + yield self.NetworkVirtualAppliancesPrepareMigration(ctx=self.ctx)() + self.post_operations() + + @register_callback + def pre_operations(self): + pass + + @register_callback + def post_operations(self): + pass + + class NetworkVirtualAppliancesPrepareMigration(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.Network/networkVirtualAppliances/{networkVirtualApplianceName}/prepareMigration", + **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( + "networkVirtualApplianceName", self.ctx.args.network_virtual_appliance_name, + required=True, + ), + **self.serialize_url_param( + "resourceGroupName", self.ctx.args.resource_group, + required=True, + ), + **self.serialize_url_param( + "subscriptionId", self.ctx.subscription_id, + required=True, + ), + } + return parameters + + @property + def query_parameters(self): + parameters = { + **self.serialize_query_param( + "api-version", "2025-09-01", + required=True, + ), + } + return parameters + + @property + def header_parameters(self): + parameters = { + **self.serialize_header_param( + "Content-Type", "application/json", + ), + **self.serialize_header_param( + "Accept", "application/json", + ), + } + return parameters + + @property + def content(self): + _content_value, _builder = self.new_content_builder( + self.ctx.args, + typ=AAZObjectType, + typ_kwargs={"flags": {"client_flatten": True}} + ) + _builder.set_prop("properties", AAZObjectType, typ_kwargs={"flags": {"client_flatten": True}}) + + properties = _builder.get(".properties") + if properties is not None: + properties.set_prop("marketPlaceVersion", AAZStrType, ".marketplace_version") + properties.set_prop("migrationType", AAZStrType, ".migration_type", typ_kwargs={"flags": {"required": True}}) + + return self.serialize_content(_content_value) + + def on_200(self, session): + pass + + +class _PrepareHelper: + """Helper class for Prepare""" + + +__all__ = ["Prepare"] diff --git a/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py b/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py index eea8415d1a1..9f2683abb34 100644 --- a/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py +++ b/src/azure-cli/azure/cli/command_modules/network/tests/latest/test_network_commands.py @@ -8747,6 +8747,98 @@ def test_network_virtual_appliance_reimage(self, resource_group): self.check('provisioningState', 'Succeeded') ]) +class NetworkVirtualApplianceMigrationScenarioTest(ScenarioTest): + @live_only() + @ResourceGroupPreparer(location='eastus2euap', name_prefix='test_network_virtual_appliance_migration') + @AllowLargeResponse(size_kb=9999) + def test_network_virtual_appliance_migration(self, resource_group): + from time import sleep + + # Variables to use in the test + subscriptionId = self.get_subscription_id() + self.kwargs.update({ + 'vwan': 'clitestvwan', # Virtual WAN name + 'vhub': 'clivhub', # Virtual Hub name + 'nva_name': 'clivirtualappliancemigration', # NVA name + 'rg': resource_group, + 'subscription': subscriptionId, + 'migration_type': 'MigrateToNewILBArchitecture', + }) + + # Add the required extension + self.cmd('extension add -n virtual-wan') + + # Create Virtual WAN + self.cmd('network vwan create -n {vwan} -g {rg} --type Standard', checks=[ + self.check('name', '{vwan}'), + self.check('type', 'Microsoft.Network/virtualWans') + ]) + + # Create Virtual Hub within the Virtual WAN + self.cmd('network vhub create -g {rg} -n {vhub} --vwan {vwan} --address-prefix 10.5.0.0/16 --sku Standard', checks=[ + self.check('name', '{vhub}'), + ]) + + routing_state = self.cmd('network vhub show -g {rg} -n {vhub}').get_output_in_json()['routingState'] + retry_count = 0 + while routing_state != 'Provisioned': + if retry_count == 20: + break + retry_count += 1 + sleep(360) + routing_state = self.cmd('network vhub show -g {rg} -n {vhub}').get_output_in_json()['routingState'] + + # Create the NVA + self.cmd('network virtual-appliance create -n {nva_name} -g {rg} --vhub {vhub} --vendor "checkpoint" ' + '--scale-unit 2 -v latest --asn 64512 --init-config "echo $abc"', + checks=[ + self.check('name', '{nva_name}'), + self.check('virtualApplianceAsn', 64512), + self.check('cloudInitConfiguration', 'echo $abc') + ]) + + # Phase 1: prepare the migration to the new ILB architecture + self.cmd('network virtual-appliance migration prepare -g {rg} -n {nva_name} --migration-type {migration_type}') + + provisioning_state = self.cmd('network virtual-appliance show -g {rg} -n {nva_name}').get_output_in_json()['provisioningState'] + retry_count = 0 + while provisioning_state != 'Succeeded': + if retry_count == 20: + raise Exception(f"Prepare migration did not complete successfully. Last known provisioningState: {provisioning_state}") + retry_count += 1 + sleep(60) + provisioning_state = self.cmd('network virtual-appliance show -g {rg} -n {nva_name}').get_output_in_json()['provisioningState'] + + # Phase 2: execute the migration + self.cmd('network virtual-appliance migration execute -g {rg} -n {nva_name} --migration-type {migration_type}') + + provisioning_state = self.cmd('network virtual-appliance show -g {rg} -n {nva_name}').get_output_in_json()['provisioningState'] + retry_count = 0 + while provisioning_state != 'Succeeded': + if retry_count == 20: + raise Exception(f"Execute migration did not complete successfully. Last known provisioningState: {provisioning_state}") + retry_count += 1 + sleep(60) + provisioning_state = self.cmd('network virtual-appliance show -g {rg} -n {nva_name}').get_output_in_json()['provisioningState'] + + # Phase 3: commit the migration to finalize the new ILB architecture + self.cmd('network virtual-appliance migration commit -g {rg} -n {nva_name} --migration-type {migration_type}') + + # Ensure that the provisioning state is 'Succeeded' after committing the migration + provisioning_state = self.cmd('network virtual-appliance show -g {rg} -n {nva_name}').get_output_in_json()['provisioningState'] + retry_count = 0 + while provisioning_state != 'Succeeded': + if retry_count == 20: + raise Exception(f"Commit migration did not complete successfully. Last known provisioningState: {provisioning_state}") + retry_count += 1 + sleep(60) + provisioning_state = self.cmd('network virtual-appliance show -g {rg} -n {nva_name}').get_output_in_json()['provisioningState'] + + self.cmd('network virtual-appliance show -g {rg} -n {nva_name}', + checks=[ + self.check('provisioningState', 'Succeeded') + ]) + class NetworkVirtualApplianceVnetScenarioTest(ScenarioTest): @AllowLargeResponse()