From fb828e5ac05d00ecfe1a5aa8878c917bfaa54aa1 Mon Sep 17 00:00:00 2001 From: Haider Agha Date: Mon, 21 Sep 2020 11:45:27 -0400 Subject: [PATCH 01/13] Checking in nested script --- .../scripts/Enable-NestedHyperV.ps1 | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 diff --git a/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 b/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 new file mode 100644 index 00000000000..702254428f9 --- /dev/null +++ b/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 @@ -0,0 +1,105 @@ +$scriptStartTime = get-date -f yyyyMMddHHmmss +$scriptPath = split-path -path $MyInvocation.MyCommand.Path -parent +$scriptName = (split-path -path $MyInvocation.MyCommand.Path -leaf).Split('.')[0] +$logFile = "$env:PUBLIC\Desktop\$($scriptName).log" +$scriptStartTime | out-file -FilePath $logFile -Append + +$nestedGuestVmName = 'ProblemVM' +$batchFile = "$env:allusersprofile\Microsoft\Windows\Start Menu\Programs\StartUp\RunHyperVManagerAndVMConnect.cmd" +$batchFileContents = @" +start $env:windir\System32\mmc.exe $env:windir\System32\virtmgmt.msc +start $env:windir\System32\vmconnect.exe localhost $nestedGuestVmName +"@ + +$features = get-windowsfeature -ErrorAction Stop +$hyperv = $features | where Name -eq 'Hyper-V' +$hypervTools = $features | where Name -eq 'Hyper-V-Tools' +$hypervPowerShell = $features | where Name -eq 'Hyper-V-Powershell' +$dhcp = $features | where Name -eq 'DHCP' +$rsatDhcp = $features | where Name -eq 'RSAT-DHCP' + +if ($hyperv.Installed -and $hypervTools.Installed -and $hypervPowerShell.Installed) +{ + "START: Creating nested guest VM" | out-file -FilePath $logFile -Append + # Sets "Do not start Server Manager automatically at logon" + $return = New-ItemProperty -Path HKLM:\Software\Microsoft\ServerManager -Name DoNotOpenServerManagerAtLogon -PropertyType DWORD -Value 1 -force -ErrorAction SilentlyContinue + $return = New-ItemProperty -Path HKLM:\Software\Microsoft\ServerManager\Oobe -Name DoNotOpenInitialConfigurationTasksAtLogon -PropertyType DWORD -Value 1 -force -ErrorAction SilentlyContinue + + try { + + # Configure NAT so nested guest has external network connectivity + # See also https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/nested-virtualization#networking-options + $switch = get-vmswitch -Name Internal -SwitchType Internal -ErrorAction SilentlyContinue | select -first 1 + if (!$switch) + { + $switch = New-VMSwitch -Name Internal -SwitchType Internal -ErrorAction Stop + } + $adapter = Get-NetAdapter -Name 'vEthernet (Internal)' -ErrorAction Stop + + $ip = get-netipaddress -IPAddress 192.168.0.1 -ErrorAction SilentlyContinue | select -first 1 + if (!$ip) + { + $return = New-NetIPAddress -IPAddress 192.168.0.1 -PrefixLength 24 -InterfaceIndex $adapter.ifIndex -ErrorAction Stop + } + + $nat = Get-NetNat -Name InternalNAT -ErrorAction SilentlyContinue | select -first 1 + if (!$nat) + { + $return = New-NetNat -Name InternalNAT -InternalIPInterfaceAddressPrefix 192.168.0.0/24 -ErrorAction Stop + } + + # Configure DHCP server service so nested guest can get an IP from DHCP and will use 168.63.129.16 for DNS and 192.168.0.1 as default gateway + if ($dhcp.Installed -eq $false -or $rsatDhcp.Installed -eq $false) + { + $return = Install-WindowsFeature -Name DHCP -IncludeManagementTools -ErrorAction Stop + } + $scope = Get-DhcpServerv4Scope -ErrorAction SilentlyContinue | where Name -eq Scope1 | select -first 1 + if (!$scope) + { + $return = Add-DhcpServerV4Scope -Name Scope1 -StartRange 192.168.0.100 -EndRange 192.168.0.200 -SubnetMask 255.255.255.0 -ErrorAction Stop + } + $return = Set-DhcpServerv4OptionValue -DnsServer 168.63.129.16 -Router 192.168.0.1 -ErrorAction Stop + + # Create the nested guest VM + $return = new-vm -name $nestedGuestVmName -MemoryStartupBytes 4GB -NoVHD -BootDevice IDE -Generation 1 -ErrorAction Stop + $return = set-vm -name $nestedGuestVmName -ProcessorCount 2 -CheckpointType Disabled -ErrorAction Stop + $disk = get-disk -ErrorAction Stop | where {$_.FriendlyName -eq 'Msft Virtual Disk'} + $return = $disk | set-disk -IsOffline $true -ErrorAction Stop + $return = $disk | Add-VMHardDiskDrive -VMName $nestedGuestVmName -ErrorAction Stop + $return = $switch | Connect-VMNetworkAdapter -VMName $nestedGuestVmName -ErrorAction Stop + $return = start-vm -Name $nestedGuestVmName -ErrorAction Stop + $nestedGuestVmState = (get-vm -Name $nestedGuestVmName -ErrorAction Stop).State + + # Create a batch file in the all users startup folder so both Hyper-V Manager and VMConnect run automatically at logon. + $return = $batchFileContents | out-file -FilePath $batchFile -Force -Encoding Default + $return = copy-item -path "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\Hyper-V Manager.lnk" -Destination "C:\Users\Public\Desktop" + # Suppress the prompt for "Do you want to allow your PC to be discoverable by other PCs and devices on this network" + $return = new-item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Network\NewNetworkWindowOff" -Force + "END: Creating nested guest VM" | out-file -FilePath $logFile -Append + } + catch { + throw $_ + exit 1 + } + + # Returns the nested guest VM status to the calling script - "Running" if all went well. + $nestedGuestVmState +} +else +{ + "START: Installing Hyper-V" | out-file -FilePath $logFile -Append + try { + # Install Hyper-V role. The required restart is handled in the calling script, not this script, to make sure that this script cleanly returns the Hyper-V role install status to the calling script. + $return = install-windowsfeature -name Hyper-V -IncludeManagementTools -ErrorAction Stop + } + catch { + throw $_ + exit 1 + } + "END: Installing Hyper-V" | out-file -FilePath $logFile -Append + $return.ExitCode + write-host $return.ExitCode +} + +$scriptEndTime = get-date -f yyyyMMddHHmmss +$scriptEndTime | out-file -FilePath $logFile -Append \ No newline at end of file From 34f9dcf675647230ad3c5844297df67c06efe620 Mon Sep 17 00:00:00 2001 From: Haider Agha Date: Thu, 8 Oct 2020 16:17:58 -0400 Subject: [PATCH 02/13] hyperv changes --- src/vm-repair/azext_vm_repair/_params.py | 1 + src/vm-repair/azext_vm_repair/custom.py | 39 ++++++++++++++++--- src/vm-repair/azext_vm_repair/repair_utils.py | 7 ++-- .../scripts/Enable-NestedHyperV.ps1 | 1 + src/vm-repair/azext_vm_repair/telemetry.py | 2 +- 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/vm-repair/azext_vm_repair/_params.py b/src/vm-repair/azext_vm_repair/_params.py index 689f0d569fc..982524642ca 100644 --- a/src/vm-repair/azext_vm_repair/_params.py +++ b/src/vm-repair/azext_vm_repair/_params.py @@ -29,6 +29,7 @@ def load_arguments(self, _): c.argument('copy_disk_name', help='Name of OS disk copy.') 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.') with self.argument_context('vm repair restore') as c: c.argument('repair_vm_id', help='Repair VM resource id.') diff --git a/src/vm-repair/azext_vm_repair/custom.py b/src/vm-repair/azext_vm_repair/custom.py index 1724fca262b..1832ae72077 100644 --- a/src/vm-repair/azext_vm_repair/custom.py +++ b/src/vm-repair/azext_vm_repair/custom.py @@ -38,8 +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): - +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): # Init command helper object command = command_helper(logger, cmd, 'vm repair create') @@ -66,7 +65,7 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern 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) # Fetch VM size of repair VM - sku = _fetch_compatible_sku(source_vm) + sku = _fetch_compatible_sku(source_vm, enable_nested) if not sku: raise SkuNotAvailableError('Failed to find compatible VM size for source VM\'s OS disk within given region and subscription.') create_repair_vm_command += ' --size {sku}'.format(sku=sku) @@ -75,7 +74,7 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern create_resource_group_command = 'az group create -l {loc} -n {group_name}' \ .format(loc=source_vm.location, group_name=repair_group_name) logger.info('Creating resource group for repair VM and its resources...') - _call_az_command(create_resource_group_command) + retval = _call_az_command(create_resource_group_command) # MANAGED DISK if is_managed: @@ -97,7 +96,7 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern copy_disk_id = _call_az_command(copy_disk_command).strip('\n') attach_disk_command = 'az vm disk attach -g {g} --vm-name {repair} --name {id}' \ - .format(g=repair_group_name, repair=repair_vm_name, id=copy_disk_id) + .format(g=repair_group_name, repair=repair_vm_name, id=copy_disk_id) logger.info('Creating repair VM...') _call_az_command(create_repair_vm_command, secure_params=[repair_password, repair_username]) @@ -158,6 +157,36 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern .format(g=repair_group_name, disk_name=copy_disk_name, vm_name=repair_vm_name, uri=copy_disk_id) _call_az_command(attach_disk_command) + #invoke enable-NestedHyperV.ps1 again to attach Disk to Nested + REPAIR_DIR_NAME = 'azext_vm_repair' + SCRIPTS_DIR_NAME = 'scripts' + loader = pkgutil.get_loader(REPAIR_DIR_NAME) + mod = loader.load_module(REPAIR_DIR_NAME) + rootpath = os.path.dirname(mod.__file__) + ENABLE_NESTED = 'Enable-NestedHyperV.ps1' + run_script = os.path.join(rootpath, SCRIPTS_DIR_NAME, ENABLE_NESTED) + + hyperv_run_command = 'az vm run-command invoke -g {rg} -n {vm} --command-id {command_id} ' \ + '--scripts "@{run_script}" -o json' \ + .format(rg=repair_group_name, vm=repair_vm_name, command_id='RunPowerShellScript', run_script=run_script) + + retval = "" + if enable_nested: + print("running hyperv") + retval = _call_az_command(hyperv_run_command) + print(retval) + + if str.find(retval, "SuccessRestartRequired") > -1: + restart_cmd = 'az vm restart -g {rg} -n {vm}'.format(rg=repair_group_name, vm=repair_vm_name) + print("restarting") + restart_ret = _call_az_command(restart_cmd) + print(restart_ret) + + if enable_nested: + print("running hyperv again") + retval = _call_az_command(hyperv_run_command) + print(retval) + created_resources = _list_resource_ids_in_rg(repair_group_name) command.set_status_success() diff --git a/src/vm-repair/azext_vm_repair/repair_utils.py b/src/vm-repair/azext_vm_repair/repair_utils.py index 8783e582eae..d7cf1465927 100644 --- a/src/vm-repair/azext_vm_repair/repair_utils.py +++ b/src/vm-repair/azext_vm_repair/repair_utils.py @@ -124,7 +124,7 @@ def _clean_up_resources(resource_group_name, confirm): logger.error("Clean up failed.") -def _fetch_compatible_sku(source_vm): +def _fetch_compatible_sku(source_vm, hyperv): location = source_vm.location source_vm_sku = source_vm.hardware_profile.vm_size @@ -145,11 +145,12 @@ def _fetch_compatible_sku(source_vm): # TODO, premium IO only when needed list_sku_command = 'az vm list-skus -s standard_d -l {loc} --query ' \ '"[?capabilities[?name==\'vCPUs\' && to_number(value)>= to_number(\'2\')] && ' \ - 'capabilities[?name==\'vCPUs\' && to_number(value)<= to_number(\'8\')] && ' \ + 'capabilities[?name==\'vCPUs\' && to_number(value)<= to_number(\'16\')] && ' \ 'capabilities[?name==\'MemoryGB\' && to_number(value)>=to_number(\'8\')] && ' \ 'capabilities[?name==\'MemoryGB\' && to_number(value)<=to_number(\'32\')] && ' \ 'capabilities[?name==\'MaxDataDiskCount\' && to_number(value)>to_number(\'0\')] && ' \ - 'capabilities[?name==\'PremiumIO\' && value==\'True\']].name" -o json'\ + 'capabilities[?name==\'PremiumIO\' && value==\'True\'] && ' \ + 'capabilities[?name==\'HyperVGenerations\' && value==\'V1\']].name" -o json'\ .format(loc=location) logger.info('Fetching available VM sizes for repair VM...') diff --git a/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 b/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 index 702254428f9..3797bdb9c4f 100644 --- a/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 +++ b/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 @@ -99,6 +99,7 @@ else "END: Installing Hyper-V" | out-file -FilePath $logFile -Append $return.ExitCode write-host $return.ExitCode + return $return.ExitCode } $scriptEndTime = get-date -f yyyyMMddHHmmss diff --git a/src/vm-repair/azext_vm_repair/telemetry.py b/src/vm-repair/azext_vm_repair/telemetry.py index 81f4da45158..b10586c5844 100644 --- a/src/vm-repair/azext_vm_repair/telemetry.py +++ b/src/vm-repair/azext_vm_repair/telemetry.py @@ -12,7 +12,7 @@ TEST_KEY = 'a6bdff92-33b5-426f-9123-33875d0ae98b' PROD_KEY = '3e7130f2-759b-41d4-afb8-f1405d1d7ed9' -tc = TelemetryClient(PROD_KEY) +tc = TelemetryClient(TEST_KEY) tc.context.application.ver = _get_current_vmrepair_version() From 38b4e96a29cd3f61427f3e338c5c16b65d88dd4f Mon Sep 17 00:00:00 2001 From: Haider Agha Date: Fri, 23 Oct 2020 00:02:07 -0400 Subject: [PATCH 03/13] adding exceptions and methods --- src/vm-repair/azext_vm_repair/custom.py | 68 +++++++++++-------- src/vm-repair/azext_vm_repair/exceptions.py | 8 +++ src/vm-repair/azext_vm_repair/repair_utils.py | 64 ++++++++++++++--- src/vm-repair/setup.py | 3 +- 4 files changed, 103 insertions(+), 40 deletions(-) diff --git a/src/vm-repair/azext_vm_repair/custom.py b/src/vm-repair/azext_vm_repair/custom.py index b7cac738578..a972f2a9978 100644 --- a/src/vm-repair/azext_vm_repair/custom.py +++ b/src/vm-repair/azext_vm_repair/custom.py @@ -30,9 +30,11 @@ _check_script_succeeded, _fetch_disk_info, _unlock_singlepass_encrypted_disk, - _invoke_run_command + _invoke_run_command, + _check_hyperV_gen, + _invoke_nested ) -from .exceptions import AzCommandError, SkuNotAvailableError, UnmanagedDiskCopyError, WindowsOsNotAvailableError, RunScriptNotFoundForIdError +from .exceptions import AzCommandError, SkuNotAvailableError, UnmanagedDiskCopyError, WindowsOsNotAvailableError, RunScriptNotFoundForIdError, SkuDoesNotSupportHyperV, ScriptReturnsError logger = get_logger(__name__) @@ -58,12 +60,17 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern else: os_image_urn = _fetch_compatible_windows_os_urn(source_vm) os_type = 'Windows' + + #check hyperv Generation + if enable_nested: + _check_hyperV_gen(source_vm) # Set up base create vm command 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) # Fetch VM size of repair VM sku = _fetch_compatible_sku(source_vm, enable_nested) + if not sku: raise SkuNotAvailableError('Failed to find compatible VM size for source VM\'s OS disk within given region and subscription.') create_repair_vm_command += ' --size {sku}'.format(sku=sku) @@ -72,7 +79,7 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern create_resource_group_command = 'az group create -l {loc} -n {group_name}' \ .format(loc=source_vm.location, group_name=repair_group_name) logger.info('Creating resource group for repair VM and its resources...') - retval = _call_az_command(create_resource_group_command) + _call_az_command(create_resource_group_command) # MANAGED DISK if is_managed: @@ -156,34 +163,29 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern _call_az_command(attach_disk_command) #invoke enable-NestedHyperV.ps1 again to attach Disk to Nested - REPAIR_DIR_NAME = 'azext_vm_repair' - SCRIPTS_DIR_NAME = 'scripts' - loader = pkgutil.get_loader(REPAIR_DIR_NAME) - mod = loader.load_module(REPAIR_DIR_NAME) - rootpath = os.path.dirname(mod.__file__) - ENABLE_NESTED = 'Enable-NestedHyperV.ps1' - run_script = os.path.join(rootpath, SCRIPTS_DIR_NAME, ENABLE_NESTED) - - hyperv_run_command = 'az vm run-command invoke -g {rg} -n {vm} --command-id {command_id} ' \ - '--scripts "@{run_script}" -o json' \ - .format(rg=repair_group_name, vm=repair_vm_name, command_id='RunPowerShellScript', run_script=run_script) - - retval = "" if enable_nested: print("running hyperv") - retval = _call_az_command(hyperv_run_command) - print(retval) - - if str.find(retval, "SuccessRestartRequired") > -1: - restart_cmd = 'az vm restart -g {rg} -n {vm}'.format(rg=repair_group_name, vm=repair_vm_name) - print("restarting") - restart_ret = _call_az_command(restart_cmd) - print(restart_ret) - - if enable_nested: - print("running hyperv again") - retval = _call_az_command(hyperv_run_command) - print(retval) + + stdout, stderr = _invoke_nested(repair_vm_name, repair_group_name) + + logger.debug("stderr: %s", stderr) + if stderr: + raise ScriptReturnsError('error when running script') + + print(stdout) + if str.find(stdout, "SuccessRestartRequired") > -1: + restart_cmd = 'az vm restart -g {rg} -n {vm}'.format(rg=repair_group_name, vm=repair_vm_name) + logger.info("restarting") + restart_ret = _call_az_command(restart_cmd) + logger.info(restart_ret) + + #invoking hyperv script again + stdout, stderr = _invoke_nested(repair_vm_name, repair_group_name) + if stderr: + raise ScriptReturnsError('Error when running script') + + logger.debug("stderr: %s", stderr) + print(stdout) created_resources = _list_resource_ids_in_rg(repair_group_name) command.set_status_success() @@ -197,6 +199,14 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern command.error_stack_trace = traceback.format_exc() command.error_message = str(azCommandError) command.message = "Repair create failed. Cleaning up created resources." + except SkuDoesNotSupportHyperV as skuDoesNotSupportHyperV: + command.error_stack_trace = traceback.format_exc() + command.error_message = str(skuDoesNotSupportHyperV) + command.message = "v2 sku does not support nested VM in hyperv. Please run command without --enabled-nested." + except ScriptReturnsError as scriptReturnsError: + command.error_stack_trace = traceback.format_exc() + command.error_message = str(scriptReturnsError) + command.message = "Error returned from script when enabling hyperv." except SkuNotAvailableError as skuNotAvailableError: command.error_stack_trace = traceback.format_exc() command.error_message = str(skuNotAvailableError) diff --git a/src/vm-repair/azext_vm_repair/exceptions.py b/src/vm-repair/azext_vm_repair/exceptions.py index 708915616d9..718d566dfad 100644 --- a/src/vm-repair/azext_vm_repair/exceptions.py +++ b/src/vm-repair/azext_vm_repair/exceptions.py @@ -22,3 +22,11 @@ class WindowsOsNotAvailableError(Exception): class RunScriptNotFoundForIdError(Exception): """Raised when the run-id is not found in the repair-script-library""" + + +class SkuDoesNotSupportHyperV(Exception): + """Raised when the SKU size does not end with v3""" + + +class ScriptReturnsError(Exception): + """Raised when run script returns error""" diff --git a/src/vm-repair/azext_vm_repair/repair_utils.py b/src/vm-repair/azext_vm_repair/repair_utils.py index be8eaaf22f3..371ce4524ef 100644 --- a/src/vm-repair/azext_vm_repair/repair_utils.py +++ b/src/vm-repair/azext_vm_repair/repair_utils.py @@ -16,7 +16,7 @@ from .encryption_types import Encryption -from .exceptions import AzCommandError, WindowsOsNotAvailableError, RunScriptNotFoundForIdError +from .exceptions import AzCommandError, WindowsOsNotAvailableError, RunScriptNotFoundForIdError, SkuDoesNotSupportHyperV # pylint: disable=line-too-long, deprecated-method REPAIR_MAP_URL = 'https://raw.githubusercontent.com/Azure/repair-script-library/master/map.json' @@ -66,6 +66,28 @@ def _call_az_command(command_string, run_async=False, secure_params=None): return stdout return None +def _invoke_nested(vm_name, rg_name): + REPAIR_DIR_NAME = 'azext_vm_repair' + SCRIPTS_DIR_NAME = 'scripts' + RUN_COMMAND_RUN_PS_ID = 'RunPowerShellScript' + + # Build absoulte path of driver script + loader = pkgutil.get_loader(REPAIR_DIR_NAME) + mod = loader.load_module(REPAIR_DIR_NAME) + rootpath = os.path.dirname(mod.__file__) + run_script = os.path.join(rootpath, SCRIPTS_DIR_NAME, 'enable-nestedhyperv.ps1') + run_command = 'az vm run-command invoke -g {rg} -n {vm} --command-id {command_id} ' \ + '--scripts @"{run_script}" -o json' \ + .format(rg=rg_name, vm=vm_name, command_id=RUN_COMMAND_RUN_PS_ID, run_script=run_script) + + return_str = _call_az_command(run_command) + + # Extract stdout and stderr, if stderr exists then possible error + run_command_return = loads(return_str) + stdout = run_command_return['value'][0]['message'] + stderr = run_command_return['value'][1]['message'] + return stdout, stderr + def _invoke_run_command(script_name, vm_name, rg_name, is_linux, parameters=None, additional_custom_scripts=None): """ @@ -177,28 +199,41 @@ def _fetch_compatible_sku(source_vm, hyperv): location = source_vm.location source_vm_sku = source_vm.hardware_profile.vm_size + if not (not source_vm_sku.endswith('v3') and hyperv): # First get the source_vm sku, if its available go with it - check_sku_command = 'az vm list-skus -s {sku} -l {loc} --query [].name -o tsv'.format(sku=source_vm_sku, loc=location) + check_sku_command = 'az vm list-skus -s {sku} -l {loc} --query [].name -o tsv'.format(sku=source_vm_sku, loc=location) - logger.info('Checking if source VM size is available...') - sku_check = _call_az_command(check_sku_command).strip('\n') + logger.info('Checking if source VM size is available...') + sku_check = _call_az_command(check_sku_command).strip('\n') - if sku_check: - logger.info('Source VM size \'%s\' is available. Using it to create repair VM.\n', source_vm_sku) - return source_vm_sku + if sku_check: + logger.info('Source VM size \'%s\' is available. Using it to create repair VM.\n', source_vm_sku) + return source_vm_sku - logger.info('Source VM size: \'%s\' is NOT available.\n', source_vm_sku) + logger.info('Source VM size: \'%s\' is NOT available.\n', source_vm_sku) # List available standard SKUs # TODO, premium IO only when needed - list_sku_command = 'az vm list-skus -s standard_d -l {loc} --query ' \ + if not hyperv: + list_sku_command = 'az vm list-skus -s standard_d -l {loc} --query ' \ + '"[?capabilities[?name==\'vCPUs\' && to_number(value)>= to_number(\'2\')] && ' \ + 'capabilities[?name==\'vCPUs\' && to_number(value)<= to_number(\'16\')] && ' \ + 'capabilities[?name==\'MemoryGB\' && to_number(value)>=to_number(\'8\')] && ' \ + 'capabilities[?name==\'MemoryGB\' && to_number(value)<=to_number(\'32\')] && ' \ + 'capabilities[?name==\'MaxDataDiskCount\' && to_number(value)>to_number(\'0\')] && ' \ + 'capabilities[?name==\'PremiumIO\' && value==\'True\'] && ' \ + 'capabilities[?name==\'HyperVGenerations\']].name" -o json ' \ + .format(loc=location) + + else: + list_sku_command = 'az vm list-skus -s _v3 -l {loc} --query ' \ '"[?capabilities[?name==\'vCPUs\' && to_number(value)>= to_number(\'2\')] && ' \ 'capabilities[?name==\'vCPUs\' && to_number(value)<= to_number(\'16\')] && ' \ 'capabilities[?name==\'MemoryGB\' && to_number(value)>=to_number(\'8\')] && ' \ 'capabilities[?name==\'MemoryGB\' && to_number(value)<=to_number(\'32\')] && ' \ 'capabilities[?name==\'MaxDataDiskCount\' && to_number(value)>to_number(\'0\')] && ' \ 'capabilities[?name==\'PremiumIO\' && value==\'True\'] && ' \ - 'capabilities[?name==\'HyperVGenerations\' && value==\'V1\']].name" -o json'\ + 'capabilities[?name==\'HyperVGenerations\']].name" -o json ' \ .format(loc=location) logger.info('Fetching available VM sizes for repair VM...') @@ -254,6 +289,15 @@ def _fetch_encryption_settings(source_vm): key_vault, kekurl, secreturl = key_vault[0], kekurl[0], secreturl[0] return Encryption.SINGLE_WITH_KEK, key_vault, kekurl, secreturl +def _check_hyperV_gen(source_vm): + disk_id = source_vm.storage_profile.os_disk.managed_disk.id + show_disk_command = 'az disk show --id {i} --query [hyperVgeneration] -o json' \ + .format(i=disk_id) + hyperVGen = loads(_call_az_command(show_disk_command)) + + if hyperVGen == 'V2': + raise SkuDoesNotSupportHyperV('Cannot support V2 HyperV generation. Please run command without --enabled-nested') + def _secret_tag_check(resource_group_name, copy_disk_name, secreturl): DEFAULT_LINUXPASSPHRASE_FILENAME = 'LinuxPassPhraseFileName' diff --git a/src/vm-repair/setup.py b/src/vm-repair/setup.py index dd93573034c..1396ecbd2c1 100644 --- a/src/vm-repair/setup.py +++ b/src/vm-repair/setup.py @@ -8,7 +8,7 @@ from codecs import open from setuptools import setup, find_packages -VERSION = "0.3.3" +VERSION = "0.3.4" CLASSIFIERS = [ 'Development Status :: 4 - Beta', @@ -42,6 +42,7 @@ 'scripts/linux-run-driver.sh', 'scripts/win-run-driver.ps1', 'scripts/mount-encrypted-disk.sh', + 'scripts/enable-nestedhyperv.ps1', 'azext_metadata.json' ] }, From 711e6201c9c37f8c7493c0cf5a61d2c26f125880 Mon Sep 17 00:00:00 2001 From: Haider Agha Date: Mon, 4 Jan 2021 09:19:23 -0500 Subject: [PATCH 04/13] logger changes --- src/vm-repair/azext_vm_repair/custom.py | 8 +++++--- .../scripts/Enable-NestedHyperV.ps1 | 15 +++++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/vm-repair/azext_vm_repair/custom.py b/src/vm-repair/azext_vm_repair/custom.py index a972f2a9978..ef904fcefe6 100644 --- a/src/vm-repair/azext_vm_repair/custom.py +++ b/src/vm-repair/azext_vm_repair/custom.py @@ -164,22 +164,24 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern #invoke enable-NestedHyperV.ps1 again to attach Disk to Nested if enable_nested: - print("running hyperv") + logger.info("Running hyperv") stdout, stderr = _invoke_nested(repair_vm_name, repair_group_name) logger.debug("stderr: %s", stderr) if stderr: + logger.debug(stderr) raise ScriptReturnsError('error when running script') - print(stdout) + logger.debug("stdout: %s", stdout) if str.find(stdout, "SuccessRestartRequired") > -1: restart_cmd = 'az vm restart -g {rg} -n {vm}'.format(rg=repair_group_name, vm=repair_vm_name) logger.info("restarting") restart_ret = _call_az_command(restart_cmd) logger.info(restart_ret) - #invoking hyperv script again + #invoking hyperv script again + logger.info("Running HyperV script again") stdout, stderr = _invoke_nested(repair_vm_name, repair_group_name) if stderr: raise ScriptReturnsError('Error when running script') diff --git a/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 b/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 index 3797bdb9c4f..3552156aec0 100644 --- a/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 +++ b/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 @@ -1,6 +1,9 @@ +. .\src\windows\common\setup\init.ps1 + $scriptStartTime = get-date -f yyyyMMddHHmmss $scriptPath = split-path -path $MyInvocation.MyCommand.Path -parent $scriptName = (split-path -path $MyInvocation.MyCommand.Path -leaf).Split('.')[0] + $logFile = "$env:PUBLIC\Desktop\$($scriptName).log" $scriptStartTime | out-file -FilePath $logFile -Append @@ -20,7 +23,7 @@ $rsatDhcp = $features | where Name -eq 'RSAT-DHCP' if ($hyperv.Installed -and $hypervTools.Installed -and $hypervPowerShell.Installed) { - "START: Creating nested guest VM" | out-file -FilePath $logFile -Append + Log-Info 'START: Creating nested guest VM' | out-file -FilePath $logFile -Append # Sets "Do not start Server Manager automatically at logon" $return = New-ItemProperty -Path HKLM:\Software\Microsoft\ServerManager -Name DoNotOpenServerManagerAtLogon -PropertyType DWORD -Value 1 -force -ErrorAction SilentlyContinue $return = New-ItemProperty -Path HKLM:\Software\Microsoft\ServerManager\Oobe -Name DoNotOpenInitialConfigurationTasksAtLogon -PropertyType DWORD -Value 1 -force -ErrorAction SilentlyContinue @@ -33,6 +36,7 @@ if ($hyperv.Installed -and $hypervTools.Installed -and $hypervPowerShell.Install if (!$switch) { $switch = New-VMSwitch -Name Internal -SwitchType Internal -ErrorAction Stop + Log-Info 'New VMSwitch Successfully created' | out-file -FilePath $logFile -Append } $adapter = Get-NetAdapter -Name 'vEthernet (Internal)' -ErrorAction Stop @@ -40,18 +44,21 @@ if ($hyperv.Installed -and $hypervTools.Installed -and $hypervPowerShell.Install if (!$ip) { $return = New-NetIPAddress -IPAddress 192.168.0.1 -PrefixLength 24 -InterfaceIndex $adapter.ifIndex -ErrorAction Stop + Log-Info 'New NetIPAddress Successfully created' | out-file -FilePath $logFile -Append } $nat = Get-NetNat -Name InternalNAT -ErrorAction SilentlyContinue | select -first 1 if (!$nat) { $return = New-NetNat -Name InternalNAT -InternalIPInterfaceAddressPrefix 192.168.0.0/24 -ErrorAction Stop + Log-Info 'New NetNat Successfully created' | out-file -FilePath $logFile -Append } # Configure DHCP server service so nested guest can get an IP from DHCP and will use 168.63.129.16 for DNS and 192.168.0.1 as default gateway if ($dhcp.Installed -eq $false -or $rsatDhcp.Installed -eq $false) { $return = Install-WindowsFeature -Name DHCP -IncludeManagementTools -ErrorAction Stop + Log-Info 'New NetIPAddress Successfully created' | out-file -FilePath $logFile -Append } $scope = Get-DhcpServerv4Scope -ErrorAction SilentlyContinue | where Name -eq Scope1 | select -first 1 if (!$scope) @@ -79,7 +86,7 @@ if ($hyperv.Installed -and $hypervTools.Installed -and $hypervPowerShell.Install } catch { throw $_ - exit 1 + return $STATUS_ERROR } # Returns the nested guest VM status to the calling script - "Running" if all went well. @@ -94,12 +101,12 @@ else } catch { throw $_ - exit 1 + return $STATUS_ERROR } "END: Installing Hyper-V" | out-file -FilePath $logFile -Append $return.ExitCode write-host $return.ExitCode - return $return.ExitCode + return $STATUS_SUCCESS } $scriptEndTime = get-date -f yyyyMMddHHmmss From 99f3363cde0ab849dae094138ee0c7663031cd24 Mon Sep 17 00:00:00 2001 From: Haider Agha Date: Tue, 26 Jan 2021 00:12:48 -0500 Subject: [PATCH 05/13] commit changes for enable-nested --- src/vm-repair/azext_vm_repair/custom.py | 6 ++--- src/vm-repair/azext_vm_repair/repair_utils.py | 23 ------------------- ...yperV.ps1 => win-enable-nested-hyperv.ps1} | 22 ++++++++---------- 3 files changed, 11 insertions(+), 40 deletions(-) rename src/vm-repair/azext_vm_repair/scripts/{Enable-NestedHyperV.ps1 => win-enable-nested-hyperv.ps1} (88%) diff --git a/src/vm-repair/azext_vm_repair/custom.py b/src/vm-repair/azext_vm_repair/custom.py index fe161f0ab10..60f2ddabaca 100644 --- a/src/vm-repair/azext_vm_repair/custom.py +++ b/src/vm-repair/azext_vm_repair/custom.py @@ -174,8 +174,7 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern if enable_nested: logger.info("Running hyperv") - stdout, stderr = _invoke_nested(repair_vm_name, repair_group_name) - + stdout, stderr = _invoke_run_command("win-enable-nested-hyperv.ps1", repair_vm_name, repair_group_name, 0) logger.debug("stderr: %s", stderr) if stderr: logger.debug(stderr) @@ -190,7 +189,7 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern #invoking hyperv script again logger.info("Running HyperV script again") - stdout, stderr = _invoke_nested(repair_vm_name, repair_group_name) + stdout, stderr = _invoke_run_command("win-enable-nested-hyperv.ps1", repair_vm_name, repair_group_name, 0) if stderr: raise ScriptReturnsError('Error when running script') @@ -347,7 +346,6 @@ def run(cmd, vm_name, resource_group_name, run_id=None, repair_vm_id=None, custo # Init command helper object command = command_helper(logger, cmd, 'vm repair run') - LINUX_RUN_SCRIPT_NAME = 'linux-run-driver.sh' WINDOWS_RUN_SCRIPT_NAME = 'win-run-driver.ps1' diff --git a/src/vm-repair/azext_vm_repair/repair_utils.py b/src/vm-repair/azext_vm_repair/repair_utils.py index 627c8e25925..13d75936f55 100644 --- a/src/vm-repair/azext_vm_repair/repair_utils.py +++ b/src/vm-repair/azext_vm_repair/repair_utils.py @@ -66,29 +66,6 @@ def _call_az_command(command_string, run_async=False, secure_params=None): return stdout return None -def _invoke_nested(vm_name, rg_name): - REPAIR_DIR_NAME = 'azext_vm_repair' - SCRIPTS_DIR_NAME = 'scripts' - RUN_COMMAND_RUN_PS_ID = 'RunPowerShellScript' - - # Build absoulte path of driver script - loader = pkgutil.get_loader(REPAIR_DIR_NAME) - mod = loader.load_module(REPAIR_DIR_NAME) - rootpath = os.path.dirname(mod.__file__) - run_script = os.path.join(rootpath, SCRIPTS_DIR_NAME, 'enable-nestedhyperv.ps1') - run_command = 'az vm run-command invoke -g {rg} -n {vm} --command-id {command_id} ' \ - '--scripts @"{run_script}" -o json' \ - .format(rg=rg_name, vm=vm_name, command_id=RUN_COMMAND_RUN_PS_ID, run_script=run_script) - - return_str = _call_az_command(run_command) - - # Extract stdout and stderr, if stderr exists then possible error - run_command_return = loads(return_str) - stdout = run_command_return['value'][0]['message'] - stderr = run_command_return['value'][1]['message'] - return stdout, stderr - - def _invoke_run_command(script_name, vm_name, rg_name, is_linux, parameters=None, additional_custom_scripts=None): """ Use azure run command to run the scripts within the vm-repair/scripts file and return stdout, stderr. diff --git a/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 b/src/vm-repair/azext_vm_repair/scripts/win-enable-nested-hyperv.ps1 similarity index 88% rename from src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 rename to src/vm-repair/azext_vm_repair/scripts/win-enable-nested-hyperv.ps1 index 3552156aec0..ae66cc50ab9 100644 --- a/src/vm-repair/azext_vm_repair/scripts/Enable-NestedHyperV.ps1 +++ b/src/vm-repair/azext_vm_repair/scripts/win-enable-nested-hyperv.ps1 @@ -1,4 +1,4 @@ -. .\src\windows\common\setup\init.ps1 +Write-Output 'Running Script Enable-NestedHyperV' $scriptStartTime = get-date -f yyyyMMddHHmmss $scriptPath = split-path -path $MyInvocation.MyCommand.Path -parent @@ -23,7 +23,7 @@ $rsatDhcp = $features | where Name -eq 'RSAT-DHCP' if ($hyperv.Installed -and $hypervTools.Installed -and $hypervPowerShell.Installed) { - Log-Info 'START: Creating nested guest VM' | out-file -FilePath $logFile -Append + Write-Output 'START: Creating nested guest VM' | out-file -FilePath $logFile -Append # Sets "Do not start Server Manager automatically at logon" $return = New-ItemProperty -Path HKLM:\Software\Microsoft\ServerManager -Name DoNotOpenServerManagerAtLogon -PropertyType DWORD -Value 1 -force -ErrorAction SilentlyContinue $return = New-ItemProperty -Path HKLM:\Software\Microsoft\ServerManager\Oobe -Name DoNotOpenInitialConfigurationTasksAtLogon -PropertyType DWORD -Value 1 -force -ErrorAction SilentlyContinue @@ -32,11 +32,11 @@ if ($hyperv.Installed -and $hypervTools.Installed -and $hypervPowerShell.Install # Configure NAT so nested guest has external network connectivity # See also https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/nested-virtualization#networking-options - $switch = get-vmswitch -Name Internal -SwitchType Internal -ErrorAction SilentlyContinue | select -first 1 + $switch = Get-VMSwitch -Name Internal -SwitchType Internal -ErrorAction SilentlyContinue | select -first 1 if (!$switch) { $switch = New-VMSwitch -Name Internal -SwitchType Internal -ErrorAction Stop - Log-Info 'New VMSwitch Successfully created' | out-file -FilePath $logFile -Append + #Log-Info 'New VMSwitch Successfully created' | out-file -FilePath $logFile -Append } $adapter = Get-NetAdapter -Name 'vEthernet (Internal)' -ErrorAction Stop @@ -44,21 +44,21 @@ if ($hyperv.Installed -and $hypervTools.Installed -and $hypervPowerShell.Install if (!$ip) { $return = New-NetIPAddress -IPAddress 192.168.0.1 -PrefixLength 24 -InterfaceIndex $adapter.ifIndex -ErrorAction Stop - Log-Info 'New NetIPAddress Successfully created' | out-file -FilePath $logFile -Append + #Log-Info 'New NetIPAddress Successfully created' | out-file -FilePath $logFile -Append } $nat = Get-NetNat -Name InternalNAT -ErrorAction SilentlyContinue | select -first 1 if (!$nat) { $return = New-NetNat -Name InternalNAT -InternalIPInterfaceAddressPrefix 192.168.0.0/24 -ErrorAction Stop - Log-Info 'New NetNat Successfully created' | out-file -FilePath $logFile -Append + #Log-Info 'New NetNat Successfully created' | out-file -FilePath $logFile -Append } # Configure DHCP server service so nested guest can get an IP from DHCP and will use 168.63.129.16 for DNS and 192.168.0.1 as default gateway if ($dhcp.Installed -eq $false -or $rsatDhcp.Installed -eq $false) { $return = Install-WindowsFeature -Name DHCP -IncludeManagementTools -ErrorAction Stop - Log-Info 'New NetIPAddress Successfully created' | out-file -FilePath $logFile -Append + #Log-Info 'New NetIPAddress Successfully created' | out-file -FilePath $logFile -Append } $scope = Get-DhcpServerv4Scope -ErrorAction SilentlyContinue | where Name -eq Scope1 | select -first 1 if (!$scope) @@ -86,11 +86,9 @@ if ($hyperv.Installed -and $hypervTools.Installed -and $hypervPowerShell.Install } catch { throw $_ - return $STATUS_ERROR } - # Returns the nested guest VM status to the calling script - "Running" if all went well. - $nestedGuestVmState + return $nestedGuestVmState } else { @@ -101,12 +99,10 @@ else } catch { throw $_ - return $STATUS_ERROR } "END: Installing Hyper-V" | out-file -FilePath $logFile -Append - $return.ExitCode write-host $return.ExitCode - return $STATUS_SUCCESS + return } $scriptEndTime = get-date -f yyyyMMddHHmmss From 1b32df698cd6a7310f1267b1b5b6aff4b1ccb51a Mon Sep 17 00:00:00 2001 From: Haider Agha Date: Tue, 26 Jan 2021 00:40:54 -0500 Subject: [PATCH 06/13] style changes --- src/vm-repair/azext_vm_repair/custom.py | 15 +++---- src/vm-repair/azext_vm_repair/repair_utils.py | 39 ++++++++++--------- src/vm-repair/azext_vm_repair/telemetry.py | 2 +- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/vm-repair/azext_vm_repair/custom.py b/src/vm-repair/azext_vm_repair/custom.py index 60f2ddabaca..68c4723806e 100644 --- a/src/vm-repair/azext_vm_repair/custom.py +++ b/src/vm-repair/azext_vm_repair/custom.py @@ -31,8 +31,7 @@ _fetch_disk_info, _unlock_singlepass_encrypted_disk, _invoke_run_command, - _check_hyperV_gen, - _invoke_nested + _check_hyperV_gen ) from .exceptions import AzCommandError, SkuNotAvailableError, UnmanagedDiskCopyError, WindowsOsNotAvailableError, RunScriptNotFoundForIdError, SkuDoesNotSupportHyperV, ScriptReturnsError logger = get_logger(__name__) @@ -59,8 +58,7 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern else: os_image_urn = _fetch_compatible_windows_os_urn(source_vm) os_type = 'Windows' - - #check hyperv Generation + # check hyperv Generation if enable_nested: _check_hyperV_gen(source_vm) @@ -69,7 +67,6 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern .format(g=repair_group_name, n=repair_vm_name, tag=resource_tag, image=os_image_urn, username=repair_username, password=repair_password) # Fetch VM size of repair VM sku = _fetch_compatible_sku(source_vm, enable_nested) - if not sku: raise SkuNotAvailableError('Failed to find compatible VM size for source VM\'s OS disk within given region and subscription.') create_repair_vm_command += ' --size {sku}'.format(sku=sku) @@ -170,7 +167,7 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern .format(g=repair_group_name, disk_name=copy_disk_name, vm_name=repair_vm_name, uri=copy_disk_id) _call_az_command(attach_disk_command) - #invoke enable-NestedHyperV.ps1 again to attach Disk to Nested + # invoke enable-NestedHyperV.ps1 again to attach Disk to Nested if enable_nested: logger.info("Running hyperv") @@ -180,15 +177,15 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern logger.debug(stderr) raise ScriptReturnsError('error when running script') - logger.debug("stdout: %s", stdout) + logger.debug("stdout: %s", stdout) if str.find(stdout, "SuccessRestartRequired") > -1: restart_cmd = 'az vm restart -g {rg} -n {vm}'.format(rg=repair_group_name, vm=repair_vm_name) logger.info("restarting") restart_ret = _call_az_command(restart_cmd) logger.info(restart_ret) - #invoking hyperv script again - logger.info("Running HyperV script again") + # invoking hyperv script again + logger.info("Running HyperV script again") stdout, stderr = _invoke_run_command("win-enable-nested-hyperv.ps1", repair_vm_name, repair_group_name, 0) if stderr: raise ScriptReturnsError('Error when running script') diff --git a/src/vm-repair/azext_vm_repair/repair_utils.py b/src/vm-repair/azext_vm_repair/repair_utils.py index 13d75936f55..7aaa4441072 100644 --- a/src/vm-repair/azext_vm_repair/repair_utils.py +++ b/src/vm-repair/azext_vm_repair/repair_utils.py @@ -66,6 +66,7 @@ def _call_az_command(command_string, run_async=False, secure_params=None): return stdout return None + def _invoke_run_command(script_name, vm_name, rg_name, is_linux, parameters=None, additional_custom_scripts=None): """ Use azure run command to run the scripts within the vm-repair/scripts file and return stdout, stderr. @@ -176,8 +177,8 @@ def _fetch_compatible_sku(source_vm, hyperv): location = source_vm.location source_vm_sku = source_vm.hardware_profile.vm_size - if not (not source_vm_sku.endswith('v3') and hyperv): # First get the source_vm sku, if its available go with it + if not (not source_vm_sku.endswith('v3') and hyperv): check_sku_command = 'az vm list-skus -s {sku} -l {loc} --query [].name -o tsv'.format(sku=source_vm_sku, loc=location) logger.info('Checking if source VM size is available...') @@ -193,25 +194,25 @@ def _fetch_compatible_sku(source_vm, hyperv): # TODO, premium IO only when needed if not hyperv: list_sku_command = 'az vm list-skus -s standard_d -l {loc} --query ' \ - '"[?capabilities[?name==\'vCPUs\' && to_number(value)>= to_number(\'2\')] && ' \ - 'capabilities[?name==\'vCPUs\' && to_number(value)<= to_number(\'16\')] && ' \ - 'capabilities[?name==\'MemoryGB\' && to_number(value)>=to_number(\'8\')] && ' \ - 'capabilities[?name==\'MemoryGB\' && to_number(value)<=to_number(\'32\')] && ' \ - 'capabilities[?name==\'MaxDataDiskCount\' && to_number(value)>to_number(\'0\')] && ' \ - 'capabilities[?name==\'PremiumIO\' && value==\'True\'] && ' \ - 'capabilities[?name==\'HyperVGenerations\']].name" -o json ' \ - .format(loc=location) - + '"[?capabilities[?name==\'vCPUs\' && to_number(value)>= to_number(\'2\')] && ' \ + 'capabilities[?name==\'vCPUs\' && to_number(value)<= to_number(\'16\')] && ' \ + 'capabilities[?name==\'MemoryGB\' && to_number(value)>=to_number(\'8\')] && ' \ + 'capabilities[?name==\'MemoryGB\' && to_number(value)<=to_number(\'32\')] && ' \ + 'capabilities[?name==\'MaxDataDiskCount\' && to_number(value)>to_number(\'0\')] && ' \ + 'capabilities[?name==\'PremiumIO\' && value==\'True\'] && ' \ + 'capabilities[?name==\'HyperVGenerations\']].name" -o json ' \ + .format(loc=location) + else: list_sku_command = 'az vm list-skus -s _v3 -l {loc} --query ' \ - '"[?capabilities[?name==\'vCPUs\' && to_number(value)>= to_number(\'2\')] && ' \ - 'capabilities[?name==\'vCPUs\' && to_number(value)<= to_number(\'16\')] && ' \ - 'capabilities[?name==\'MemoryGB\' && to_number(value)>=to_number(\'8\')] && ' \ - 'capabilities[?name==\'MemoryGB\' && to_number(value)<=to_number(\'32\')] && ' \ - 'capabilities[?name==\'MaxDataDiskCount\' && to_number(value)>to_number(\'0\')] && ' \ - 'capabilities[?name==\'PremiumIO\' && value==\'True\'] && ' \ - 'capabilities[?name==\'HyperVGenerations\']].name" -o json ' \ - .format(loc=location) + '"[?capabilities[?name==\'vCPUs\' && to_number(value)>= to_number(\'2\')] && ' \ + 'capabilities[?name==\'vCPUs\' && to_number(value)<= to_number(\'16\')] && ' \ + 'capabilities[?name==\'MemoryGB\' && to_number(value)>=to_number(\'8\')] && ' \ + 'capabilities[?name==\'MemoryGB\' && to_number(value)<=to_number(\'32\')] && ' \ + 'capabilities[?name==\'MaxDataDiskCount\' && to_number(value)>to_number(\'0\')] && ' \ + 'capabilities[?name==\'PremiumIO\' && value==\'True\'] && ' \ + 'capabilities[?name==\'HyperVGenerations\']].name" -o json ' \ + .format(loc=location) logger.info('Fetching available VM sizes for repair VM...') sku_list = loads(_call_az_command(list_sku_command).strip('\n')) @@ -266,12 +267,12 @@ def _fetch_encryption_settings(source_vm): key_vault, kekurl, secreturl = key_vault[0], kekurl[0], secreturl[0] return Encryption.SINGLE_WITH_KEK, key_vault, kekurl, secreturl + def _check_hyperV_gen(source_vm): disk_id = source_vm.storage_profile.os_disk.managed_disk.id show_disk_command = 'az disk show --id {i} --query [hyperVgeneration] -o json' \ .format(i=disk_id) hyperVGen = loads(_call_az_command(show_disk_command)) - if hyperVGen == 'V2': raise SkuDoesNotSupportHyperV('Cannot support V2 HyperV generation. Please run command without --enabled-nested') diff --git a/src/vm-repair/azext_vm_repair/telemetry.py b/src/vm-repair/azext_vm_repair/telemetry.py index b10586c5844..81f4da45158 100644 --- a/src/vm-repair/azext_vm_repair/telemetry.py +++ b/src/vm-repair/azext_vm_repair/telemetry.py @@ -12,7 +12,7 @@ TEST_KEY = 'a6bdff92-33b5-426f-9123-33875d0ae98b' PROD_KEY = '3e7130f2-759b-41d4-afb8-f1405d1d7ed9' -tc = TelemetryClient(TEST_KEY) +tc = TelemetryClient(PROD_KEY) tc.context.application.ver = _get_current_vmrepair_version() From 9bce5b7eef053e32157678b2c9f5bc331d8d0ad3 Mon Sep 17 00:00:00 2001 From: Haider Agha Date: Mon, 1 Feb 2021 10:48:31 -0500 Subject: [PATCH 07/13] validating vm is windows --- src/vm-repair/azext_vm_repair/_validators.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vm-repair/azext_vm_repair/_validators.py b/src/vm-repair/azext_vm_repair/_validators.py index 2135032b8a4..0a3b6601346 100644 --- a/src/vm-repair/azext_vm_repair/_validators.py +++ b/src/vm-repair/azext_vm_repair/_validators.py @@ -70,6 +70,10 @@ def validate_create(cmd, namespace): else: logger.debug('The source VM\'s OS disk is not encrypted') + if namespace.enable_nested: + if is_linux: + raise CLIError('Nested Vm is not supported for linux vm') + # Validate Auth Params # Prompt vm username if not namespace.repair_username: @@ -166,6 +170,8 @@ def validate_run(cmd, namespace): raise CLIError('Repair resource id is not valid.') + + def _prompt_encrypted_vm(namespace): from knack.prompting import prompt_y_n, NoTTYException try: From 8db2755bb4ee8b05d018123a41d615147e095a70 Mon Sep 17 00:00:00 2001 From: Haider Agha Date: Mon, 1 Feb 2021 11:21:41 -0500 Subject: [PATCH 08/13] styling fixes --- src/vm-repair/azext_vm_repair/_validators.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vm-repair/azext_vm_repair/_validators.py b/src/vm-repair/azext_vm_repair/_validators.py index 0a3b6601346..56070ddc476 100644 --- a/src/vm-repair/azext_vm_repair/_validators.py +++ b/src/vm-repair/azext_vm_repair/_validators.py @@ -170,8 +170,6 @@ def validate_run(cmd, namespace): raise CLIError('Repair resource id is not valid.') - - def _prompt_encrypted_vm(namespace): from knack.prompting import prompt_y_n, NoTTYException try: From b4ff15b915913b13e7113230c73daec776a45c60 Mon Sep 17 00:00:00 2001 From: haagha <64601174+haagha@users.noreply.github.com> Date: Wed, 3 Feb 2021 10:41:08 -0500 Subject: [PATCH 09/13] Update src/vm-repair/azext_vm_repair/_validators.py Co-authored-by: Feiyue Yu --- src/vm-repair/azext_vm_repair/_validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm-repair/azext_vm_repair/_validators.py b/src/vm-repair/azext_vm_repair/_validators.py index 56070ddc476..b66729d2fb3 100644 --- a/src/vm-repair/azext_vm_repair/_validators.py +++ b/src/vm-repair/azext_vm_repair/_validators.py @@ -72,7 +72,7 @@ def validate_create(cmd, namespace): if namespace.enable_nested: if is_linux: - raise CLIError('Nested Vm is not supported for linux vm') + raise CLIError('Nested VM is not supported for Linux VM') # Validate Auth Params # Prompt vm username From 20ee06e8af9974f60050b795899a3b4158da0f96 Mon Sep 17 00:00:00 2001 From: haagha <64601174+haagha@users.noreply.github.com> Date: Wed, 3 Feb 2021 10:41:17 -0500 Subject: [PATCH 10/13] Update src/vm-repair/azext_vm_repair/custom.py Co-authored-by: Feiyue Yu --- src/vm-repair/azext_vm_repair/custom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm-repair/azext_vm_repair/custom.py b/src/vm-repair/azext_vm_repair/custom.py index 68c4723806e..61d302538d7 100644 --- a/src/vm-repair/azext_vm_repair/custom.py +++ b/src/vm-repair/azext_vm_repair/custom.py @@ -175,7 +175,7 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern logger.debug("stderr: %s", stderr) if stderr: logger.debug(stderr) - raise ScriptReturnsError('error when running script') + raise ScriptReturnsError('Error when running script') logger.debug("stdout: %s", stdout) if str.find(stdout, "SuccessRestartRequired") > -1: From c674ec3b39701736aa242685a323c648bda4e5c2 Mon Sep 17 00:00:00 2001 From: Haider Agha Date: Mon, 8 Mar 2021 17:35:29 -0500 Subject: [PATCH 11/13] Using run command to enable nested VM --- src/vm-repair/azext_vm_repair/custom.py | 36 ++++++++++++------------- src/vm-repair/setup.py | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/vm-repair/azext_vm_repair/custom.py b/src/vm-repair/azext_vm_repair/custom.py index 68c4723806e..9e9104163fb 100644 --- a/src/vm-repair/azext_vm_repair/custom.py +++ b/src/vm-repair/azext_vm_repair/custom.py @@ -169,29 +169,29 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern # invoke enable-NestedHyperV.ps1 again to attach Disk to Nested if enable_nested: - logger.info("Running hyperv") - - stdout, stderr = _invoke_run_command("win-enable-nested-hyperv.ps1", repair_vm_name, repair_group_name, 0) - logger.debug("stderr: %s", stderr) - if stderr: - logger.debug(stderr) - raise ScriptReturnsError('error when running script') - - logger.debug("stdout: %s", stdout) - if str.find(stdout, "SuccessRestartRequired") > -1: + logger.info("Running Script win-enable-nested-hyperv.ps1 to install HyperV") + + run_hyperv_command = "az vm repair run -g {g} -n {name} --run-id win-enable-nested-hyperv" \ + .format(g=repair_group_name, name=repair_vm_name) + ret_enable_nested = _call_az_command(run_hyperv_command) + + #if stdout: + #raise ScriptReturnsError('Error in installing win-enable-nested-hyperv.ps1 Script') + logger.debug("az vm repair run hyperv command returned: %s", ret_enable_nested) + + if str.find(ret_enable_nested, "SuccessRestartRequired") > -1: restart_cmd = 'az vm restart -g {rg} -n {vm}'.format(rg=repair_group_name, vm=repair_vm_name) - logger.info("restarting") + logger.info("Restarting Repair VM") restart_ret = _call_az_command(restart_cmd) - logger.info(restart_ret) + logger.debug(restart_ret) # invoking hyperv script again - logger.info("Running HyperV script again") - stdout, stderr = _invoke_run_command("win-enable-nested-hyperv.ps1", repair_vm_name, repair_group_name, 0) - if stderr: - raise ScriptReturnsError('Error when running script') + logger.info("Running win-enable-nested-hyperv.ps1 again to create nested VM") + run_hyperv_command = "az vm repair run -g {g} -n {name} --run-id win-enable-nested-hyperv" \ + .format(g=repair_group_name, name=repair_vm_name) + ret_enable_nested_again = _call_az_command(run_hyperv_command) - logger.debug("stderr: %s", stderr) - print(stdout) + logger.debug("stderr: %s", ret_enable_nested_again) created_resources = _list_resource_ids_in_rg(repair_group_name) command.set_status_success() diff --git a/src/vm-repair/setup.py b/src/vm-repair/setup.py index 334c63e64a6..933f1adba42 100644 --- a/src/vm-repair/setup.py +++ b/src/vm-repair/setup.py @@ -8,7 +8,7 @@ from codecs import open from setuptools import setup, find_packages -VERSION = "0.3.4" +VERSION = "0.3.5" CLASSIFIERS = [ 'Development Status :: 4 - Beta', From f624477ba13beaebc523986fc8f58576976bfd27 Mon Sep 17 00:00:00 2001 From: Haider Agha Date: Mon, 8 Mar 2021 17:38:36 -0500 Subject: [PATCH 12/13] removing status --- src/vm-repair/azext_vm_repair/custom.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vm-repair/azext_vm_repair/custom.py b/src/vm-repair/azext_vm_repair/custom.py index 9e9104163fb..0d05a9e573c 100644 --- a/src/vm-repair/azext_vm_repair/custom.py +++ b/src/vm-repair/azext_vm_repair/custom.py @@ -175,8 +175,6 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern .format(g=repair_group_name, name=repair_vm_name) ret_enable_nested = _call_az_command(run_hyperv_command) - #if stdout: - #raise ScriptReturnsError('Error in installing win-enable-nested-hyperv.ps1 Script') logger.debug("az vm repair run hyperv command returned: %s", ret_enable_nested) if str.find(ret_enable_nested, "SuccessRestartRequired") > -1: From 9ecce9ca02e8f1886ff822a69ffec0535ef147f1 Mon Sep 17 00:00:00 2001 From: Haider Agha Date: Mon, 8 Mar 2021 17:49:10 -0500 Subject: [PATCH 13/13] Style Check --- src/vm-repair/azext_vm_repair/custom.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vm-repair/azext_vm_repair/custom.py b/src/vm-repair/azext_vm_repair/custom.py index 0d05a9e573c..9915279d651 100644 --- a/src/vm-repair/azext_vm_repair/custom.py +++ b/src/vm-repair/azext_vm_repair/custom.py @@ -170,11 +170,11 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern # invoke enable-NestedHyperV.ps1 again to attach Disk to Nested if enable_nested: logger.info("Running Script win-enable-nested-hyperv.ps1 to install HyperV") - + run_hyperv_command = "az vm repair run -g {g} -n {name} --run-id win-enable-nested-hyperv" \ - .format(g=repair_group_name, name=repair_vm_name) + .format(g=repair_group_name, name=repair_vm_name) ret_enable_nested = _call_az_command(run_hyperv_command) - + logger.debug("az vm repair run hyperv command returned: %s", ret_enable_nested) if str.find(ret_enable_nested, "SuccessRestartRequired") > -1: @@ -186,7 +186,7 @@ def create(cmd, vm_name, resource_group_name, repair_password=None, repair_usern # invoking hyperv script again logger.info("Running win-enable-nested-hyperv.ps1 again to create nested VM") run_hyperv_command = "az vm repair run -g {g} -n {name} --run-id win-enable-nested-hyperv" \ - .format(g=repair_group_name, name=repair_vm_name) + .format(g=repair_group_name, name=repair_vm_name) ret_enable_nested_again = _call_az_command(run_hyperv_command) logger.debug("stderr: %s", ret_enable_nested_again)