Skip to content

Commit b78af08

Browse files
author
Razvan Ivan
committed
Making admin commands work for serial console when the VM has a custom storage account attached.
1 parent 1e7a157 commit b78af08

File tree

4 files changed

+109
-94
lines changed

4 files changed

+109
-94
lines changed

src/serial-console/HISTORY.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
Release History
22
===============
3+
1.0.1
4+
++++++
5+
* Fixed an issue where admin commands were not being sent when the VM was using a custom boot diagnostics storage account.
6+
37
1.0.0b2
48
++++++
59
* Changed to 2024 API version, fixes Disable API to track "properties". Essentially return to 2018 format

src/serial-console/azext_serialconsole/custom.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,9 @@ def wrapper():
551551
if self.load_websocket_url():
552552
def on_message(ws, _):
553553
GV.trycount += 1
554+
if GV.first_message:
555+
ws.send(self.access_token)
556+
GV.first_message = False
554557
if func():
555558
GV.loading = False
556559
GV.terminating_app = True

src/serial-console/azext_serialconsole/tests/latest/test_serialconsole_scenario.py

Lines changed: 101 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,8 @@ def test_enable_disable(self):
197197

198198
class SerialconsoleAdminCommandsTest(LiveScenarioTest):
199199

200-
def check_result(self, resource_group_name, vm_vmss_name, vmss_instanceid=None, message=""):
200+
def check_result(self, resource_group_name, vm_vmss_name, vmss_instanceid=None, message="", hasManagedStorageAccount=False):
201+
print("Checking serial console output for message: ", message)
201202
ARM_ENDPOINT = "https://management.azure.com"
202203
RP_PROVIDER = "Microsoft.SerialConsole"
203204
subscription_id = self.get_subscription_id()
@@ -217,19 +218,53 @@ def check_result(self, resource_group_name, vm_vmss_name, vmss_instanceid=None,
217218
'content-type': application_json_format}
218219
result = requests.post(connection_url, headers=headers)
219220
json_results = json.loads(result.text)
220-
self.assertTrue(result.status_code ==
221-
200 and "connectionString" in json_results)
221+
222+
if (not result.status_code == 200) or ("connectionString" not in json_results):
223+
print("Failed to get connection string from serial console connect endpoint.")
224+
print("Status code: ", result.status_code)
225+
print("Response text: ", result.text)
226+
self.fail("Failed to get connection string from serial console connect endpoint. See status and response in logs.")
227+
222228
websocket_url = json_results["connectionString"]
223229

224230
ws = websocket.WebSocket()
225231
ws.connect(websocket_url + "?authorization=" + access_token, timeout=30)
232+
print("WebSocket connected for verifying message.")
233+
print("Sleeping 60 seconds to allow 'Connecting...' message to appear.")
234+
time.sleep(60)
235+
236+
if not hasManagedStorageAccount:
237+
print("Sending access token to start custom storage account setup...")
238+
ws.send(access_token)
239+
print("Finished sending access token")
240+
226241
buffer = ""
242+
iter = 0
243+
print("Starting to read from WebSocket to find message...")
227244
while True:
228-
try:
229-
buffer += ws.recv()
230-
except (websocket.WebSocketTimeoutException, websocket.WebSocketConnectionClosedException):
245+
iter += 1
246+
print(f"Current timestamp: {time.strftime('%X')}, current iteration: {iter}/5")
247+
248+
while True:
249+
try:
250+
buffer += ws.recv()
251+
except (websocket.WebSocketTimeoutException, websocket.WebSocketConnectionClosedException):
252+
break
253+
254+
if message in buffer:
255+
print("Found message in buffer! Finished verification.")
231256
break
232-
257+
258+
print(f"Message not found yet in buffer, current buffer: {buffer}")
259+
260+
if iter >= 10:
261+
print("Max retries reached, exiting read loop.")
262+
break
263+
else:
264+
print("Sleeping 10 seconds before retrying...")
265+
time.sleep(10)
266+
267+
ws.close()
233268
assert message in buffer
234269

235270
@ResourceGroupPreparer(name_prefix='cli_test_serialconsole', location='westus2')
@@ -243,27 +278,13 @@ def test_send_sysrq_VMSS(self, resource_group, storage_account):
243278
'urn': 'Ubuntu2204',
244279
'loc': 'westus2'
245280
})
246-
self.cmd(
247-
'az vmss create -g {rg} -n {name} --image {urn} --instance-count 2 -l {loc} --orchestration-mode uniform')
248-
self.cmd('az vmss update --name {name} --resource-group {rg} --set virtualMachineProfile.diagnosticsProfile="{{\\"bootDiagnostics\\": {{\\"Enabled\\" : \\"True\\",\\"StorageUri\\":\\"https://{sa}.blob.core.windows.net/\\"}}}}"')
249-
result = self.cmd(
250-
'vmss list-instances --resource-group {rg} --name {name} --query "[].instanceId"').get_output_in_json()
251-
self.kwargs.update({'id': result[1]})
252-
self.cmd(
253-
'az vmss update-instances -g {rg} -n {name} --instance-ids {id}')
254-
time.sleep(60)
255-
for i in range(5):
256-
try:
257-
self.cmd('vmss get-instance-view --resource-group {rg} --name {name} --instance-id {id}', checks=[
258-
self.check('statuses[0].code',
259-
'ProvisioningState/succeeded'),
260-
self.check('statuses[1].code', 'PowerState/running'),
261-
])
262-
break
263-
except JMESPathCheckAssertionError:
264-
time.sleep(30)
281+
result = self.createVMSS()
282+
print("Sending SysRq...")
283+
stopwatch_start = time.time()
265284
self.cmd(
266285
'serial-console send sysrq -g {rg} -n {name} --instance-id {id} --input h')
286+
stopwatch_end = time.time()
287+
print(f"SysRq sent in {stopwatch_end - stopwatch_start} seconds.")
267288
self.check_result(resource_group, name,
268289
vmss_instanceid=result[1], message="sysrq: HELP")
269290

@@ -278,27 +299,13 @@ def test_send_nmi_VMSS(self, resource_group, storage_account):
278299
'urn': 'Ubuntu2204',
279300
'loc': 'westus2'
280301
})
281-
self.cmd(
282-
'az vmss create -g {rg} -n {name} --image {urn} --instance-count 2 -l {loc} --orchestration-mode uniform')
283-
self.cmd('az vmss update --name {name} --resource-group {rg} --set virtualMachineProfile.diagnosticsProfile="{{\\"bootDiagnostics\\": {{\\"Enabled\\" : \\"True\\",\\"StorageUri\\":\\"https://{sa}.blob.core.windows.net/\\"}}}}"')
284-
result = self.cmd(
285-
'vmss list-instances --resource-group {rg} --name {name} --query "[].instanceId"').get_output_in_json()
286-
self.kwargs.update({'id': result[1]})
287-
self.cmd(
288-
'az vmss update-instances -g {rg} -n {name} --instance-ids {id}')
289-
time.sleep(60)
290-
for i in range(5):
291-
try:
292-
self.cmd('vmss get-instance-view --resource-group {rg} --name {name} --instance-id {id}', checks=[
293-
self.check('statuses[0].code',
294-
'ProvisioningState/succeeded'),
295-
self.check('statuses[1].code', 'PowerState/running'),
296-
])
297-
break
298-
except JMESPathCheckAssertionError:
299-
time.sleep(30)
302+
result = self.createVMSS()
303+
print("Sending NMI...")
304+
stopwatch_start = time.time()
300305
self.cmd(
301306
'serial-console send nmi -g {rg} -n {name} --instance-id {id}')
307+
stopwatch_end = time.time()
308+
print(f"NMI sent in {stopwatch_end - stopwatch_start} seconds.")
302309
self.check_result(resource_group, name,
303310
vmss_instanceid=result[1], message="NMI received")
304311

@@ -313,26 +320,14 @@ def test_send_reset_VMSS(self, resource_group, storage_account):
313320
'urn': 'Ubuntu2204',
314321
'loc': 'westus2'
315322
})
316-
self.cmd(
317-
'az vmss create -g {rg} -n {name} --image {urn} --instance-count 2 -l {loc} --orchestration-mode uniform')
318-
self.cmd('az vmss update --name {name} --resource-group {rg} --set virtualMachineProfile.diagnosticsProfile="{{\\"bootDiagnostics\\": {{\\"Enabled\\" : \\"True\\",\\"StorageUri\\":\\"https://{sa}.blob.core.windows.net/\\"}}}}"')
319-
result = self.cmd(
320-
'vmss list-instances --resource-group {rg} --name {name} --query "[].instanceId"').get_output_in_json()
321-
self.kwargs.update({'id': result[1]})
322-
self.cmd(
323-
'az vmss update-instances -g {rg} -n {name} --instance-ids {id}')
324-
time.sleep(60)
325-
for i in range(5):
326-
try:
327-
self.cmd('vmss get-instance-view --resource-group {rg} --name {name} --instance-id {id}', checks=[
328-
self.check('statuses[0].code',
329-
'ProvisioningState/succeeded'),
330-
self.check('statuses[1].code', 'PowerState/running'),
331-
])
332-
break
333-
except JMESPathCheckAssertionError:
334-
time.sleep(30)
323+
result = self.createVMSS()
324+
print("Sending Reset...")
325+
stopwatch_start = time.time()
335326
self.cmd('serial-console send reset -g {rg} -n {name} --instance-id {id}')
327+
stopwatch_end = time.time()
328+
print(f"Reset sent in {stopwatch_end - stopwatch_start} seconds.")
329+
self.check_result(resource_group, name,
330+
vmss_instanceid=result[1], message="Record successful boot")
336331

337332
@ResourceGroupPreparer(name_prefix='cli_test_serialconsole', location='westus2')
338333
@StorageAccountPreparer(name_prefix='cli', location="westus2")
@@ -345,21 +340,12 @@ def test_send_nmi_VM(self, resource_group, storage_account):
345340
'urn': 'Ubuntu2204',
346341
'loc': 'westus2'
347342
})
348-
self.cmd(
349-
'az vm create -g {rg} -n {name} --image {urn} --boot-diagnostics-storage {sa} -l {loc} --generate-ssh-keys')
350-
time.sleep(60)
351-
for i in range(5):
352-
try:
353-
self.cmd('vm get-instance-view --resource-group {rg} --name {name}', checks=[
354-
self.check(
355-
'instanceView.statuses[0].code', 'ProvisioningState/succeeded'),
356-
self.check(
357-
'instanceView.statuses[1].code', 'PowerState/running'),
358-
])
359-
break
360-
except JMESPathCheckAssertionError:
361-
time.sleep(30)
343+
self.createVM()
344+
print("Sending NMI...")
345+
stopwatch_start = time.time()
362346
self.cmd('serial-console send nmi -g {rg} -n {name}')
347+
stopwatch_end = time.time()
348+
print(f"NMI sent in {stopwatch_end - stopwatch_start} seconds.")
363349
self.check_result(resource_group, name, message="NMI received")
364350

365351
@ResourceGroupPreparer(name_prefix='cli_test_serialconsole', location='westus2')
@@ -373,21 +359,12 @@ def test_send_sysrq_VM(self, resource_group, storage_account):
373359
'urn': 'Ubuntu2204',
374360
'loc': 'westus2'
375361
})
376-
self.cmd(
377-
'az vm create -g {rg} -n {name} --image {urn} --boot-diagnostics-storage {sa} -l {loc} --generate-ssh-keys')
378-
time.sleep(60)
379-
for i in range(5):
380-
try:
381-
self.cmd('vm get-instance-view --resource-group {rg} --name {name}', checks=[
382-
self.check(
383-
'instanceView.statuses[0].code', 'ProvisioningState/succeeded'),
384-
self.check(
385-
'instanceView.statuses[1].code', 'PowerState/running'),
386-
])
387-
break
388-
except JMESPathCheckAssertionError:
389-
time.sleep(30)
362+
self.createVM()
363+
print("Sending Sysrq...")
364+
stopwatch_start = time.time()
390365
self.cmd('serial-console send sysrq -g {rg} -n {name} --input h')
366+
stopwatch_end = time.time()
367+
print(f"Sysrq sent in {stopwatch_end - stopwatch_start} seconds.")
391368
self.check_result(resource_group, name, message="sysrq: HELP")
392369

393370
@ResourceGroupPreparer(name_prefix='cli_test_serialconsole', location='westus2')
@@ -401,6 +378,38 @@ def test_send_reset_VM(self, resource_group, storage_account):
401378
'urn': 'Ubuntu2204',
402379
'loc': 'westus2'
403380
})
381+
self.createVM()
382+
print("Sending Reset...")
383+
stopwatch_start = time.time()
384+
self.cmd('serial-console send reset -g {rg} -n {name}')
385+
stopwatch_end = time.time()
386+
print(f"Reset sent in {stopwatch_end - stopwatch_start} seconds.")
387+
self.check_result(resource_group, name, message="Record successful boot")
388+
389+
def createVMSS(self):
390+
self.cmd(
391+
'az vmss create -g {rg} -n {name} --image {urn} --instance-count 2 -l {loc} --orchestration-mode uniform')
392+
self.cmd(
393+
'az vmss update --name {name} --resource-group {rg} --set virtualMachineProfile.diagnosticsProfile="{{\\"bootDiagnostics\\": {{\\"Enabled\\" : \\"True\\",\\"StorageUri\\":\\"https://{sa}.blob.core.windows.net/\\"}}}}"')
394+
result = self.cmd(
395+
'vmss list-instances --resource-group {rg} --name {name} --query "[].instanceId"').get_output_in_json()
396+
self.kwargs.update({'id': result[1]})
397+
self.cmd(
398+
'az vmss update-instances -g {rg} -n {name} --instance-ids {id}')
399+
time.sleep(60)
400+
for i in range(5):
401+
try:
402+
self.cmd('vmss get-instance-view --resource-group {rg} --name {name} --instance-id {id}', checks=[
403+
self.check('statuses[0].code',
404+
'ProvisioningState/succeeded'),
405+
self.check('statuses[1].code', 'PowerState/running'),
406+
])
407+
break
408+
except JMESPathCheckAssertionError:
409+
time.sleep(30)
410+
return result
411+
412+
def createVM(self):
404413
self.cmd(
405414
'az vm create -g {rg} -n {name} --image {urn} --boot-diagnostics-storage {sa} -l {loc} --generate-ssh-keys')
406415
time.sleep(60)
@@ -415,4 +424,3 @@ def test_send_reset_VM(self, resource_group, storage_account):
415424
break
416425
except JMESPathCheckAssertionError:
417426
time.sleep(30)
418-
self.cmd('serial-console send reset -g {rg} -n {name}')

src/serial-console/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
# TODO: Confirm this is the right version number you want and it matches your
1818
# HISTORY.rst entry.
19-
VERSION = '1.0.0b2'
19+
VERSION = '1.0.1'
2020

2121
# The full list of classifiers is available at
2222
# https://pypi.python.org/pypi?%3Aaction=list_classifiers

0 commit comments

Comments
 (0)