@@ -132,7 +132,7 @@ def prompt(self, getch, message):
132132 c = getch ()
133133 self .hide_cursor (buffer = False )
134134 for _ in range (lines ):
135- self .clear_line (buffer = False )
135+ # self.clear_line(buffer=False)
136136 self .cursor_up (buffer = False )
137137 self .set_cursor_horizontal_position (col , buffer = False )
138138 self .show_cursor (buffer = False )
@@ -198,8 +198,8 @@ def _getch_windows(self):
198198
199199class Terminal :
200200 ERROR_MESSAGE = "Unable to configure terminal."
201- RECOMENDATION = ("Make sure that app in running in a terminal on a Windows 10 "
202- "or Unix based machine. Versions earlier than Windows 10 are not supported." )
201+ RECOMMENDATION = ("Make sure that app in running in a terminal on a Windows 10 "
202+ "or Unix based machine. Versions earlier than Windows 10 are not supported." )
203203
204204 def __init__ (self ):
205205 self .win_original_out_mode = None
@@ -232,7 +232,7 @@ def configure_terminal(self):
232232 if (not kernel32 .GetConsoleMode (self .win_out , ctypes .byref (dw_original_out_mode )) or
233233 not kernel32 .GetConsoleMode (self .win_in , ctypes .byref (dw_original_in_mode ))):
234234 quitapp (error_message = Terminal .ERROR_MESSAGE ,
235- error_recommendation = Terminal .RECOMENDATION , error_func = UnclassifiedUserFault )
235+ error_recommendation = Terminal .RECOMMENDATION , error_func = UnclassifiedUserFault )
236236
237237 self .win_original_out_mode = dw_original_out_mode .value
238238 self .win_original_in_mode = dw_original_in_mode .value
@@ -244,15 +244,15 @@ def configure_terminal(self):
244244 if (not kernel32 .SetConsoleMode (self .win_out , dw_out_mode ) or
245245 not kernel32 .SetConsoleMode (self .win_in , dw_in_mode )):
246246 quitapp (error_message = Terminal .ERROR_MESSAGE ,
247- error_recommendation = Terminal .RECOMENDATION , error_func = UnclassifiedUserFault )
247+ error_recommendation = Terminal .RECOMMENDATION , error_func = UnclassifiedUserFault )
248248 else :
249249 try :
250250 import tty
251251 import termios # pylint: disable=import-error
252252 fd = sys .stdin .fileno ()
253253 except (ModuleNotFoundError , ValueError ):
254254 quitapp (error_message = Terminal .ERROR_MESSAGE ,
255- error_recommendation = Terminal .RECOMENDATION , error_func = UnclassifiedUserFault )
255+ error_recommendation = Terminal .RECOMMENDATION , error_func = UnclassifiedUserFault )
256256
257257 self .unix_original_mode = termios .tcgetattr (fd )
258258 tty .setraw (fd )
@@ -277,7 +277,13 @@ def revert_terminal(self):
277277
278278class SerialConsole :
279279 def __init__ (self , cmd , resource_group_name , vm_vmss_name , vmss_instanceid ):
280- client = cf_serial_port (cmd .cli_ctx )
280+ result , storage_account_region = get_region_from_storage_account (cmd .cli_ctx , resource_group_name ,
281+ vm_vmss_name , vmss_instanceid )
282+ if storage_account_region is not None :
283+ kwargs = {'storage_account_region' : storage_account_region }
284+ else :
285+ kwargs = {}
286+ client = cf_serial_port (cmd .cli_ctx , ** kwargs )
281287 if vmss_instanceid is None :
282288 self .connect_func = lambda : client .connect (
283289 resource_group_name = resource_group_name ,
@@ -365,7 +371,7 @@ def connect_loading_message_linux():
365371 chars_copy = chars .copy ()
366372 chars_copy [indx ] = "\u25A0 "
367373 squares = " " .join (chars_copy )
368- PC .clear_line ()
374+ # PC.clear_line()
369375 PC .print ("Connecting to console of VM " +
370376 squares , color = PrintClass .CYAN )
371377 PC .show_cursor ()
@@ -457,7 +463,7 @@ def connect_thread():
457463 GV .websocket_instance .run_forever (skip_utf8_validation = True )
458464 else :
459465 GV .loading = False
460- message = ("\r \n An unexpected error occured . Could not establish connection to VM or VMSS. "
466+ message = ("\r \n An unexpected error occurred . Could not establish connection to VM or VMSS. "
461467 "Check network connection and press \" Enter\" to try again..." )
462468 PC .print (message , color = PrintClass .RED )
463469
@@ -524,6 +530,7 @@ def connect_and_send_admin_command(self, command, arg_characters=None):
524530 elif command == "sysrq" and arg_characters is not None :
525531 def wrapper ():
526532 return self .send_sys_rq (arg_characters )
533+
527534 func = wrapper
528535 success_message = "Successfully sent SysRq command\r \n "
529536 failure_message = "Failed to send SysRq command. Make sure the input only contains numbers and letters.\r \n "
@@ -563,14 +570,18 @@ def on_message(ws, _):
563570 error_message , recommendation = recommendation )
564571 else :
565572 GV .loading = False
566- error_message = "An unexpected error occured . Could not establish connection to VM or VMSS."
573+ error_message = "An unexpected error occurred . Could not establish connection to VM or VMSS."
567574 recommendation = "Check network connection and try again."
568575 raise ResourceNotFoundError (
569576 error_message , recommendation = recommendation )
570577
571578
572- def check_serial_console_enabled (cli_ctx ):
573- client = cf_serialconsole (cli_ctx )
579+ def check_serial_console_enabled (cli_ctx , storage_account_region = None ):
580+ if storage_account_region is not None :
581+ kwargs = {'storage_account_region' : storage_account_region }
582+ else :
583+ kwargs = {}
584+ client = cf_serialconsole (cli_ctx , ** kwargs )
574585 result = client .get_console_status ().additional_properties
575586 if ("properties" in result and "disabled" in result ["properties" ] and
576587 not result ["properties" ]["disabled" ]):
@@ -581,11 +592,11 @@ def check_serial_console_enabled(cli_ctx):
581592
582593
583594def check_resource (cli_ctx , resource_group_name , vm_vmss_name , vmss_instanceid ):
584- check_serial_console_enabled (cli_ctx )
585- client = _compute_client_factory (cli_ctx )
595+ result , storage_account_region = get_region_from_storage_account (cli_ctx , resource_group_name , vm_vmss_name ,
596+ vmss_instanceid )
597+ check_serial_console_enabled (cli_ctx , storage_account_region )
598+
586599 if vmss_instanceid :
587- result = client .virtual_machine_scale_set_vms .get_instance_view (
588- resource_group_name , vm_vmss_name , vmss_instanceid )
589600 if 'osName' in result .additional_properties and "windows" in result .additional_properties ['osName' ].lower ():
590601 GV .os_is_windows = True
591602
@@ -596,32 +607,7 @@ def check_resource(cli_ctx, resource_group_name, vm_vmss_name, vmss_instanceid):
596607 recommendation = 'Use "az vmss start" to start the Virtual Machine.'
597608 raise AzureConnectionError (
598609 error_message , recommendation = recommendation )
599-
600- if result .boot_diagnostics is None :
601- error_message = ("Azure Serial Console requires boot diagnostics to be enabled." )
602- recommendation = ('Use "az vmss update --name MyScaleSet --resource-group MyResourceGroup --set '
603- 'virtualMachineProfile.diagnosticsProfile="{\\ "bootDiagnostics\\ ": {\\ "Enabled\\ " : '
604- '\\ "True\\ ",\\ "StorageUri\\ " : null}}"" to enable boot diagnostics. '
605- 'You can replace "null" with a custom storage account '
606- '\\ "https://mystor.blob.windows.net/"\\ . Then run "az vmss update-instances -n '
607- 'MyScaleSet -g MyResourceGroup --instance-ids *".' )
608- raise AzureConnectionError (
609- error_message , recommendation = recommendation )
610610 else :
611- try :
612- result = client .virtual_machines .get (
613- resource_group_name , vm_vmss_name , expand = 'instanceView' )
614- except ComputeClientResourceNotFoundError as e :
615- try :
616- client .virtual_machine_scale_sets .get (
617- resource_group_name , vm_vmss_name )
618- except ComputeClientResourceNotFoundError :
619- raise e from e
620- error_message = e .message
621- recommendation = ("We found that you specified a Virtual Machine Scale Set and not a VM. "
622- "Use the --instance-id parameter to select the VMSS instance you want to connect to." )
623- raise ResourceNotFoundError (
624- error_message , recommendation = recommendation ) from e
625611 if (result .instance_view is not None and
626612 result .instance_view .os_name is not None and
627613 "windows" in result .instance_view .os_name .lower ()):
@@ -640,16 +626,6 @@ def check_resource(cli_ctx, resource_group_name, vm_vmss_name, vmss_instanceid):
640626 raise AzureConnectionError (
641627 error_message , recommendation = recommendation )
642628
643- if (result .diagnostics_profile is None or
644- result .diagnostics_profile .boot_diagnostics is None or
645- not result .diagnostics_profile .boot_diagnostics .enabled ):
646- error_message = ("Azure Serial Console requires boot diagnostics to be enabled." )
647- recommendation = ('Use "az vm boot-diagnostics enable --name MyVM --resource-group MyResourceGroup" '
648- 'to enable boot diagnostics. You can specify a custom storage account with the '
649- 'parameter "--storage https://mystor.blob.windows.net/".' )
650- raise AzureConnectionError (
651- error_message , recommendation = recommendation )
652-
653629
654630def connect_serialconsole (cmd , resource_group_name , vm_vmss_name , vmss_instanceid = None ):
655631 check_resource (cmd .cli_ctx , resource_group_name ,
@@ -695,3 +671,94 @@ def enable_serialconsole(cmd):
695671def disable_serialconsole (cmd ):
696672 client = cf_serialconsole (cmd .cli_ctx )
697673 return client .disable_console ()
674+
675+
676+ def get_region_from_storage_account (cli_ctx , resource_group_name , vm_vmss_name , vmss_instanceid ):
677+ from azext_serialconsole ._client_factory import storage_client_factory
678+ from knack .log import get_logger
679+
680+ logger = get_logger (__name__ )
681+ result = None
682+ storage_account_region = None
683+ client = _compute_client_factory (cli_ctx )
684+ scf = storage_client_factory (cli_ctx )
685+
686+ if vmss_instanceid :
687+ result_data = client .virtual_machine_scale_set_vms .get_instance_view (
688+ resource_group_name , vm_vmss_name , vmss_instanceid )
689+ result = result_data
690+
691+ if result_data .boot_diagnostics is None :
692+ error_message = "Azure Serial Console requires boot diagnostics to be enabled."
693+ recommendation = ('Use "az vmss update --name MyScaleSet --resource-group MyResourceGroup --set '
694+ 'virtualMachineProfile.diagnosticsProfile="{\\ "bootDiagnostics\\ ": {\\ "Enabled\\ " : '
695+ '\\ "True\\ ",\\ "StorageUri\\ " : null}}"" to enable boot diagnostics. '
696+ 'You can replace "null" with a custom storage account '
697+ '\\ "https://mystor.blob.windows.net/"\\ . Then run "az vmss update-instances -n '
698+ 'MyScaleSet -g MyResourceGroup --instance-ids *".' )
699+ raise AzureConnectionError (
700+ error_message , recommendation = recommendation )
701+ else :
702+ if result .boot_diagnostics is not None :
703+ logger .debug (result .boot_diagnostics )
704+ if result .boot_diagnostics .console_screenshot_blob_uri is not None :
705+ storage_account_url = result .boot_diagnostics .console_screenshot_blob_uri
706+ storage_account_region = get_storage_account_info (storage_account_url , resource_group_name , scf )
707+ else :
708+ try :
709+ result_data = client .virtual_machines .get (
710+ resource_group_name , vm_vmss_name , expand = 'instanceView' )
711+ result = result_data
712+ except ComputeClientResourceNotFoundError as e :
713+ try :
714+ client .virtual_machine_scale_sets .get (resource_group_name , vm_vmss_name )
715+ except ComputeClientResourceNotFoundError :
716+ raise e from e
717+ error_message = e .message
718+ recommendation = ("We found that you specified a Virtual Machine Scale Set and not a VM. "
719+ "Use the --instance-id parameter to select the VMSS instance you want to connect to." )
720+ raise ResourceNotFoundError (
721+ error_message , recommendation = recommendation ) from e
722+
723+ if (result .diagnostics_profile is None or
724+ result .diagnostics_profile .boot_diagnostics is None or
725+ not result .diagnostics_profile .boot_diagnostics .enabled ):
726+ error_message = "Azure Serial Console requires boot diagnostics to be enabled."
727+ recommendation = ('Use "az vm boot-diagnostics enable --name MyVM --resource-group MyResourceGroup" '
728+ 'to enable boot diagnostics. You can specify a custom storage account with the '
729+ 'parameter "--storage https://mystor.blob.windows.net/".' )
730+ raise AzureConnectionError (
731+ error_message , recommendation = recommendation )
732+ else :
733+ if result .diagnostics_profile is not None :
734+ if result .diagnostics_profile .boot_diagnostics is not None :
735+ storage_account_url = result .diagnostics_profile .boot_diagnostics .storage_uri
736+ storage_account_region = get_storage_account_info (storage_account_url , resource_group_name , scf )
737+
738+ return result , storage_account_region
739+
740+
741+ def get_storage_account_info (storage_account_url , resource_group_name , scf ):
742+ from azext_serialconsole ._arm_endpoints import ArmEndpoints
743+
744+ if storage_account_url is not None :
745+ storage_account = parse_storage_account_url (storage_account_url )
746+ if storage_account is not None :
747+ sa_result = scf .storage_accounts .get_properties (resource_group_name , storage_account )
748+ if (sa_result is not None and
749+ sa_result .network_rule_set is not None and
750+ len (sa_result .network_rule_set .ip_rules ) > 0 ):
751+ return ArmEndpoints .region_prefix_pairings [sa_result .location ]
752+
753+ return None
754+
755+
756+ def parse_storage_account_url (url ):
757+ if url is not None :
758+ sa_list = url .split ('.' )
759+ if len (sa_list ) > 0 :
760+ sa_url = sa_list [0 ]
761+ sa_url = sa_url .replace ("https://" , "" )
762+ return sa_url
763+
764+ return None
0 commit comments