Skip to content

Commit

Permalink
[vm-repair] Making Public IP as optional (#3755)
Browse files Browse the repository at this point in the history
* public ipaddress as optional for rescue VMs

* public ip address for rescue vm

* public ip addr change for rescue vms

* Reverted telemetry back to PROD

* fixed code style

* Add extra space before operator on line 36 as suggested by azdev style check

* modified telemetryclient from TEST to PROD

* removed duplicate entry for associate_public_ip param

* specified specific error type in prompt_public_ip function

* included class for testing publicip association on repair vm

Co-authored-by: root <root@ubuntu18.vdtdwq5rkvyuhaj2iq0ntqetub.syx.internal.cloudapp.net>
  • Loading branch information
haagha and root authored Aug 12, 2021
1 parent 2872c55 commit 9a648fc
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/vm-repair/azext_vm_repair/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def load_arguments(self, _):
c.argument('repair_group_name', help='Repair resource group name.')
c.argument('unlock_encrypted_vm', help='Option to auto-unlock encrypted VMs using current subscription auth.')
c.argument('enable_nested', help='enable nested hyperv.')
c.argument('associate_public_ip', help='Option to create repair vm with public ip')

with self.argument_context('vm repair restore') as c:
c.argument('repair_vm_id', help='Repair VM resource id.')
Expand Down
16 changes: 16 additions & 0 deletions src/vm-repair/azext_vm_repair/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from re import match, search, findall
from knack.log import get_logger
from knack.util import CLIError
from azure.cli.core.azclierror import ValidationError

from azure.cli.command_modules.vm.custom import get_vm, _is_linux_os
from azure.cli.command_modules.resource._client_factory import _resource_client_factory
Expand Down Expand Up @@ -85,6 +86,9 @@ def validate_create(cmd, namespace):
_prompt_repair_password(namespace)
# Validate vm password
validate_vm_password(namespace.repair_password, is_linux)
# Prompt input for public ip usage
if not namespace.associate_public_ip:
_prompt_public_ip(namespace)


def validate_restore(cmd, namespace):
Expand Down Expand Up @@ -201,6 +205,18 @@ def _prompt_repair_password(namespace):
raise CLIError('Please specify the password parameter in non-interactive mode.')


def _prompt_public_ip(namespace):
from knack.prompting import prompt_y_n, NoTTYException
try:
if prompt_y_n('Does repair vm requires public ip?'):
namespace.associate_public_ip = "yes"
else:
namespace.associate_public_ip = '""'

except NoTTYException:
raise ValidationError('Please specify the associate-public-ip parameter in non-interactive mode.')


def _classic_vm_exists(cmd, resource_group_name, vm_name):
classic_vm_provider = 'Microsoft.ClassicCompute'
vm_resource_type = 'virtualMachines'
Expand Down
10 changes: 5 additions & 5 deletions src/vm-repair/azext_vm_repair/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
logger = get_logger(__name__)


def create(cmd, vm_name, resource_group_name, repair_password=None, repair_username=None, repair_vm_name=None, copy_disk_name=None, repair_group_name=None, unlock_encrypted_vm=False, enable_nested=False):
def create(cmd, vm_name, resource_group_name, repair_password=None, repair_username=None, repair_vm_name=None, copy_disk_name=None, repair_group_name=None, unlock_encrypted_vm=False, enable_nested=False, associate_public_ip=False):
# Init command helper object
command = command_helper(logger, cmd, 'vm repair create')
# Main command calling block
Expand All @@ -65,11 +65,11 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern

# Set up base create vm command
if is_linux:
create_repair_vm_command = 'az vm create -g {g} -n {n} --tag {tag} --image {image} --admin-username {username} --admin-password {password} --custom-data {cloud_init_script}' \
.format(g=repair_group_name, n=repair_vm_name, tag=resource_tag, image=os_image_urn, username=repair_username, password=repair_password, cloud_init_script=_get_cloud_init_script())
create_repair_vm_command = 'az vm create -g {g} -n {n} --tag {tag} --image {image} --admin-username {username} --admin-password {password} --public-ip-address {option} --custom-data {cloud_init_script}' \
.format(g=repair_group_name, n=repair_vm_name, tag=resource_tag, image=os_image_urn, username=repair_username, password=repair_password, option=associate_public_ip, cloud_init_script=_get_cloud_init_script())
else:
create_repair_vm_command = 'az vm create -g {g} -n {n} --tag {tag} --image {image} --admin-username {username} --admin-password {password}' \
.format(g=repair_group_name, n=repair_vm_name, tag=resource_tag, image=os_image_urn, username=repair_username, password=repair_password)
create_repair_vm_command = 'az vm create -g {g} -n {n} --tag {tag} --image {image} --admin-username {username} --admin-password {password} --public-ip-address {option}' \
.format(g=repair_group_name, n=repair_vm_name, tag=resource_tag, image=os_image_urn, username=repair_username, password=repair_password, option=associate_public_ip)

# Fetch VM size of repair VM
sku = _fetch_compatible_sku(source_vm, enable_nested)
Expand Down
136 changes: 136 additions & 0 deletions src/vm-repair/azext_vm_repair/tests/latest/test_repair_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,142 @@ def test_vmrepair_LinuxUnmanagedCreateRestore(self, resource_group):
assert source_vm['storageProfile']['osDisk']['vhd']['uri'] == result['copied_disk_uri']


class WindowsManagedDiskCreateRestoreTestwithpublicip(LiveScenarioTest):

@ResourceGroupPreparer(location='westus2')
def test_vmrepair_WinManagedCreateRestore(self, resource_group):
self.kwargs.update({
'vm': 'vm1'
})

# Create test VM
self.cmd('vm create -g {rg} -n {vm} --admin-username azureadmin --image Win2016Datacenter --admin-password !Passw0rd2018')
vms = self.cmd('vm list -g {rg} -o json').get_output_in_json()
# Something wrong with vm create command if it fails here
assert len(vms) == 1

# Test create
result = self.cmd('vm repair create -g {rg} -n {vm} --repair-username azureadmin --repair-password !Passw0rd2018 --associate-public-ip -o json').get_output_in_json()
assert result['status'] == STATUS_SUCCESS, result['error_message']

# Check repair VM
repair_vms = self.cmd('vm list -g {} -o json'.format(result['repair_resource_group'])).get_output_in_json()
assert len(repair_vms) == 1
repair_vm = repair_vms[0]
# Check attached data disk
assert repair_vm['storageProfile']['dataDisks'][0]['name'] == result['copied_disk_name']

# Call Restore
self.cmd('vm repair restore -g {rg} -n {vm} --yes')

# Check swapped OS disk
vms = self.cmd('vm list -g {rg} -o json').get_output_in_json()
source_vm = vms[0]
assert source_vm['storageProfile']['osDisk']['name'] == result['copied_disk_name']


class WindowsUnmanagedDiskCreateRestoreTestwithpublicip(LiveScenarioTest):

@ResourceGroupPreparer(location='westus2')
def test_vmrepair_WinUnmanagedCreateRestore(self, resource_group):
self.kwargs.update({
'vm': 'vm1'
})

# Create test VM
self.cmd('vm create -g {rg} -n {vm} --admin-username azureadmin --image Win2016Datacenter --admin-password !Passw0rd2018 --use-unmanaged-disk')
vms = self.cmd('vm list -g {rg} -o json').get_output_in_json()
# Something wrong with vm create command if it fails here
assert len(vms) == 1

# Test create
result = self.cmd('vm repair create -g {rg} -n {vm} --repair-username azureadmin --repair-password !Passw0rd2018 --associate-public-ip -o json').get_output_in_json()
assert result['status'] == STATUS_SUCCESS, result['error_message']

# Check repair VM
repair_vms = self.cmd('vm list -g {} -o json'.format(result['repair_resource_group'])).get_output_in_json()
assert len(repair_vms) == 1
repair_vm = repair_vms[0]
# Check attached data disk
assert repair_vm['storageProfile']['dataDisks'][0]['name'] == result['copied_disk_name']

# Call Restore
self.cmd('vm repair restore -g {rg} -n {vm} --yes')

# Check swapped OS disk
vms = self.cmd('vm list -g {rg} -o json').get_output_in_json()
source_vm = vms[0]
assert source_vm['storageProfile']['osDisk']['vhd']['uri'] == result['copied_disk_uri']


class LinuxManagedDiskCreateRestoreTestwithpublicip(LiveScenarioTest):

@ResourceGroupPreparer(location='westus2')
def test_vmrepair_LinuxManagedCreateRestore(self, resource_group):
self.kwargs.update({
'vm': 'vm1'
})

# Create test VM
self.cmd('vm create -g {rg} -n {vm} --image UbuntuLTS --admin-username azureadmin --admin-password !Passw0rd2018')
vms = self.cmd('vm list -g {rg} -o json').get_output_in_json()
# Something wrong with vm create command if it fails here
assert len(vms) == 1

# Test create
result = self.cmd('vm repair create -g {rg} -n {vm} --repair-username azureadmin --repair-password !Passw0rd2018 --associate-public-ip -o json').get_output_in_json()
assert result['status'] == STATUS_SUCCESS, result['error_message']

# Check repair VM
repair_vms = self.cmd('vm list -g {} -o json'.format(result['repair_resource_group'])).get_output_in_json()
assert len(repair_vms) == 1
repair_vm = repair_vms[0]
# Check attached data disk
assert repair_vm['storageProfile']['dataDisks'][0]['name'] == result['copied_disk_name']

# Call Restore
self.cmd('vm repair restore -g {rg} -n {vm} --yes')

# Check swapped OS disk
vms = self.cmd('vm list -g {rg} -o json').get_output_in_json()
source_vm = vms[0]
assert source_vm['storageProfile']['osDisk']['name'] == result['copied_disk_name']


class LinuxUnmanagedDiskCreateRestoreTestwithpublicip(LiveScenarioTest):

@ResourceGroupPreparer(location='westus2')
def test_vmrepair_LinuxUnmanagedCreateRestore(self, resource_group):
self.kwargs.update({
'vm': 'vm1'
})

# Create test VM
self.cmd('vm create -g {rg} -n {vm} --image UbuntuLTS --admin-username azureadmin --admin-password !Passw0rd2018 --use-unmanaged-disk')
vms = self.cmd('vm list -g {rg} -o json').get_output_in_json()
# Something wrong with vm create command if it fails here
assert len(vms) == 1

# Test create
result = self.cmd('vm repair create -g {rg} -n {vm} --repair-username azureadmin --repair-password !Passw0rd2018 --associate-public-ip -o json').get_output_in_json()
assert result['status'] == STATUS_SUCCESS, result['error_message']

# Check repair VM
repair_vms = self.cmd('vm list -g {} -o json'.format(result['repair_resource_group'])).get_output_in_json()
assert len(repair_vms) == 1
repair_vm = repair_vms[0]
# Check attached data disk
assert repair_vm['storageProfile']['dataDisks'][0]['name'] == result['copied_disk_name']

# Call Restore
self.cmd('vm repair restore -g {rg} -n {vm} --yes')

# Check swapped OS disk
vms = self.cmd('vm list -g {rg} -o json').get_output_in_json()
source_vm = vms[0]
assert source_vm['storageProfile']['osDisk']['vhd']['uri'] == result['copied_disk_uri']


class WindowsSinglepassKekEncryptedManagedDiskCreateRestoreTest(LiveScenarioTest):

@ResourceGroupPreparer(location='westus2')
Expand Down
2 changes: 1 addition & 1 deletion src/vm-repair/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
name='vm-repair',
version=VERSION,
description='Auto repair commands to fix VMs.',
long_description='VM repair command will enable Azure users to self-repair non-bootable VMs by copying the source VM\'s OS disk and attaching it to a newly created repair VM.'+ '\n\n' + HISTORY,
long_description='VM repair command will enable Azure users to self-repair non-bootable VMs by copying the source VM\'s OS disk and attaching it to a newly created repair VM.' + '\n\n' + HISTORY,
license='MIT',
author='Microsoft Corporation',
author_email='caiddev@microsoft.com',
Expand Down

0 comments on commit 9a648fc

Please sign in to comment.