Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/firecracker
Path: blob/main/tests/integration_tests/functional/test_mmds.py
1958 views
1
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
# SPDX-License-Identifier: Apache-2.0
3
"""Tests that verify MMDS related functionality."""
4
5
import json
6
import random
7
import string
8
import host_tools.network as net_tools
9
10
11
def _assert_out(stdout, stderr, expected):
12
assert stderr.read() == ''
13
assert stdout.read() == expected
14
15
16
def test_custom_ipv4(test_microvm_with_ssh, network_config):
17
"""Test the API for MMDS custom ipv4 support."""
18
test_microvm = test_microvm_with_ssh
19
test_microvm.spawn()
20
21
response = test_microvm.mmds.get()
22
assert test_microvm.api_session.is_status_ok(response.status_code)
23
assert response.json() == {}
24
25
data_store = {
26
'latest': {
27
'meta-data': {
28
'ami-id': 'ami-12345678',
29
'reservation-id': 'r-fea54097',
30
'local-hostname': 'ip-10-251-50-12.ec2.internal',
31
'public-hostname': 'ec2-203-0-113-25.compute-1.amazonaws.com',
32
'network': {
33
'interfaces': {
34
'macs': {
35
'02:29:96:8f:6a:2d': {
36
'device-number': '13345342',
37
'local-hostname': 'localhost',
38
'subnet-id': 'subnet-be9b61d'
39
}
40
}
41
}
42
}
43
}
44
}
45
}
46
response = test_microvm.mmds.put(json=data_store)
47
assert test_microvm.api_session.is_status_no_content(response.status_code)
48
49
response = test_microvm.mmds.get()
50
assert test_microvm.api_session.is_status_ok(response.status_code)
51
assert response.json() == data_store
52
53
config_data = {
54
'ipv4_address': ''
55
}
56
response = test_microvm.mmds.put_config(json=config_data)
57
assert test_microvm.api_session.is_status_bad_request(response.status_code)
58
59
config_data = {
60
'ipv4_address': '1.1.1.1'
61
}
62
response = test_microvm.mmds.put_config(json=config_data)
63
assert test_microvm.api_session.is_status_bad_request(response.status_code)
64
65
config_data = {
66
'ipv4_address': '169.254.169.250'
67
}
68
response = test_microvm.mmds.put_config(json=config_data)
69
assert test_microvm.api_session.is_status_no_content(response.status_code)
70
71
test_microvm.basic_config(vcpu_count=1)
72
_tap = test_microvm.ssh_network_config(
73
network_config,
74
'1',
75
allow_mmds_requests=True
76
)
77
78
test_microvm.start()
79
ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config)
80
81
response = test_microvm.mmds.put_config(json=config_data)
82
assert test_microvm.api_session.is_status_bad_request(response.status_code)
83
84
cmd = 'ip route add 169.254.169.250 dev eth0'
85
_, stdout, stderr = ssh_connection.execute_command(cmd)
86
_assert_out(stdout, stderr, '')
87
88
pre = 'curl -s -H "Accept: application/json" http://169.254.169.250/'
89
90
cmd = pre + 'latest/meta-data/ami-id'
91
_, stdout, _ = ssh_connection.execute_command(cmd)
92
assert json.load(stdout) == 'ami-12345678'
93
94
# The request is still valid if we append a
95
# trailing slash to a leaf node.
96
cmd = pre + 'latest/meta-data/ami-id/'
97
_, stdout, _ = ssh_connection.execute_command(cmd)
98
assert json.load(stdout) == 'ami-12345678'
99
100
cmd = pre + 'latest/meta-data/network/interfaces/macs/'\
101
'02:29:96:8f:6a:2d/subnet-id'
102
_, stdout, _ = ssh_connection.execute_command(cmd)
103
assert json.load(stdout) == 'subnet-be9b61d'
104
105
# Test reading a non-leaf node WITHOUT a trailing slash.
106
cmd = pre + 'latest/meta-data'
107
_, stdout, _ = ssh_connection.execute_command(cmd)
108
assert json.load(stdout) == data_store['latest']['meta-data']
109
110
# Test reading a non-leaf node with a trailing slash.
111
cmd = pre + 'latest/meta-data/'
112
_, stdout, _ = ssh_connection.execute_command(cmd)
113
assert json.load(stdout) == data_store['latest']['meta-data']
114
115
116
def test_json_response(test_microvm_with_ssh, network_config):
117
"""Test the MMDS json response."""
118
test_microvm = test_microvm_with_ssh
119
test_microvm.spawn()
120
121
response = test_microvm.mmds.get()
122
assert test_microvm.api_session.is_status_ok(response.status_code)
123
assert response.json() == {}
124
125
data_store = {
126
'latest': {
127
'meta-data': {
128
'ami-id': 'ami-12345678',
129
'reservation-id': 'r-fea54097',
130
'local-hostname': 'ip-10-251-50-12.ec2.internal',
131
'public-hostname': 'ec2-203-0-113-25.compute-1.amazonaws.com',
132
'dummy_res': ['res1', 'res2']
133
},
134
"Limits": {
135
"CPU": 512,
136
"Memory": 512
137
},
138
"Usage": {
139
"CPU": 12.12
140
}
141
}
142
}
143
response = test_microvm.mmds.put(json=data_store)
144
assert test_microvm.api_session.is_status_no_content(response.status_code)
145
146
response = test_microvm.mmds.get()
147
assert test_microvm.api_session.is_status_ok(response.status_code)
148
assert response.json() == data_store
149
150
test_microvm.basic_config(vcpu_count=1)
151
_tap = test_microvm.ssh_network_config(
152
network_config,
153
'1',
154
allow_mmds_requests=True
155
)
156
157
test_microvm.start()
158
ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config)
159
160
cmd = 'ip route add 169.254.169.254 dev eth0'
161
_, stdout, stderr = ssh_connection.execute_command(cmd)
162
_assert_out(stdout, stderr, '')
163
164
pre = 'curl -s -H "Accept: application/json" http://169.254.169.254/'
165
166
cmd = pre + 'latest/meta-data/'
167
_, stdout, _ = ssh_connection.execute_command(cmd)
168
assert json.load(stdout) == data_store['latest']['meta-data']
169
170
cmd = pre + 'latest/meta-data/ami-id/'
171
_, stdout, stderr = ssh_connection.execute_command(cmd)
172
assert json.load(stdout) == 'ami-12345678'
173
174
cmd = pre + 'latest/meta-data/dummy_res/0'
175
_, stdout, stderr = ssh_connection.execute_command(cmd)
176
assert json.load(stdout) == 'res1'
177
178
cmd = pre + 'latest/Usage/CPU'
179
_, stdout, stderr = ssh_connection.execute_command(cmd)
180
assert json.load(stdout) == 12.12
181
182
cmd = pre + 'latest/Limits/CPU'
183
_, stdout, stderr = ssh_connection.execute_command(cmd)
184
assert json.load(stdout) == 512
185
186
187
def test_imds_response(test_microvm_with_ssh, network_config):
188
"""Test the MMDS IMDS response."""
189
test_microvm = test_microvm_with_ssh
190
test_microvm.spawn()
191
192
response = test_microvm.mmds.get()
193
assert test_microvm.api_session.is_status_ok(response.status_code)
194
assert response.json() == {}
195
196
data_store = {
197
'latest': {
198
'meta-data': {
199
'ami-id': 'ami-12345678',
200
'reservation-id': 'r-fea54097',
201
'local-hostname': 'ip-10-251-50-12.ec2.internal',
202
'public-hostname': 'ec2-203-0-113-25.compute-1.amazonaws.com',
203
'dummy_obj': {
204
'res_key': 'res_value',
205
},
206
'dummy_array': [
207
'arr_val1',
208
'arr_val2'
209
]
210
},
211
"Limits": {
212
"CPU": 512,
213
"Memory": 512
214
},
215
"Usage": {
216
"CPU": 12.12
217
}
218
}
219
}
220
response = test_microvm.mmds.put(json=data_store)
221
assert test_microvm.api_session.is_status_no_content(response.status_code)
222
223
response = test_microvm.mmds.get()
224
assert test_microvm.api_session.is_status_ok(response.status_code)
225
assert response.json() == data_store
226
227
test_microvm.basic_config(vcpu_count=1)
228
_tap = test_microvm.ssh_network_config(
229
network_config,
230
'1',
231
allow_mmds_requests=True
232
)
233
234
test_microvm.start()
235
ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config)
236
237
cmd = 'ip route add 169.254.169.254 dev eth0'
238
_, stdout, stderr = ssh_connection.execute_command(cmd)
239
_assert_out(stdout, stderr, '')
240
241
pre = 'curl -s http://169.254.169.254/'
242
243
cmd = pre + 'latest/meta-data/'
244
_, stdout, stderr = ssh_connection.execute_command(cmd)
245
expected = "ami-id\n" \
246
"dummy_array\n"\
247
"dummy_obj/\n"\
248
"local-hostname\n"\
249
"public-hostname\n"\
250
"reservation-id"
251
252
_assert_out(stdout, stderr, expected)
253
254
cmd = pre + 'latest/meta-data/ami-id/'
255
_, stdout, stderr = ssh_connection.execute_command(cmd)
256
_assert_out(stdout, stderr, 'ami-12345678')
257
258
cmd = pre + 'latest/meta-data/dummy_array/0'
259
_, stdout, stderr = ssh_connection.execute_command(cmd)
260
_assert_out(stdout, stderr, 'arr_val1')
261
262
cmd = pre + 'latest/Usage/CPU'
263
_, stdout, stderr = ssh_connection.execute_command(cmd)
264
_assert_out(stdout, stderr, 'Cannot retrieve value. The value has an'
265
' unsupported type.')
266
267
cmd = pre + 'latest/Limits/CPU'
268
_, stdout, stderr = ssh_connection.execute_command(cmd)
269
_assert_out(stdout, stderr, 'Cannot retrieve value. The value has an'
270
' unsupported type.')
271
272
273
def test_larger_than_mss_payloads(test_microvm_with_ssh, network_config):
274
"""Test MMDS content for payloads larger than MSS."""
275
test_microvm = test_microvm_with_ssh
276
test_microvm.spawn()
277
278
# The MMDS is empty at this point.
279
response = test_microvm.mmds.get()
280
assert test_microvm.api_session.is_status_ok(response.status_code)
281
assert response.json() == {}
282
283
test_microvm.basic_config(vcpu_count=1)
284
_tap = test_microvm.ssh_network_config(
285
network_config,
286
'1',
287
allow_mmds_requests=True
288
)
289
290
test_microvm.start()
291
292
# Make sure MTU is 1500 bytes.
293
ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config)
294
295
cmd = 'ip link set dev eth0 mtu 1500'
296
_, stdout, stderr = ssh_connection.execute_command(cmd)
297
_assert_out(stdout, stderr, "")
298
299
cmd = 'ip a s eth0 | grep -i mtu | tr -s " " | cut -d " " -f 4,5'
300
_, stdout, stderr = ssh_connection.execute_command(cmd)
301
_assert_out(stdout, stderr, "mtu 1500\n")
302
303
# These values are usually used by booted up guest network interfaces.
304
mtu = 1500
305
ipv4_packet_headers_len = 20
306
tcp_segment_headers_len = 20
307
mss = mtu - ipv4_packet_headers_len - tcp_segment_headers_len
308
309
# Generate a random MMDS content, double of MSS.
310
letters = string.ascii_lowercase
311
larger_than_mss = ''.join(random.choice(letters) for i in range(2 * mss))
312
mss_equal = ''.join(random.choice(letters) for i in range(mss))
313
lower_than_mss = ''.join(random.choice(letters) for i in range(mss - 2))
314
data_store = {
315
'larger_than_mss': larger_than_mss,
316
'mss_equal': mss_equal,
317
'lower_than_mss': lower_than_mss
318
}
319
response = test_microvm.mmds.put(json=data_store)
320
assert test_microvm.api_session.is_status_no_content(response.status_code)
321
322
response = test_microvm.mmds.get()
323
assert test_microvm.api_session.is_status_ok(response.status_code)
324
assert response.json() == data_store
325
326
cmd = 'ip route add 169.254.169.254 dev eth0'
327
_, stdout, stderr = ssh_connection.execute_command(cmd)
328
_assert_out(stdout, stderr, '')
329
330
pre = 'curl -s http://169.254.169.254/'
331
332
cmd = pre + 'larger_than_mss'
333
_, stdout, stderr = ssh_connection.execute_command(cmd)
334
_assert_out(stdout, stderr, larger_than_mss)
335
336
cmd = pre + 'mss_equal'
337
_, stdout, stderr = ssh_connection.execute_command(cmd)
338
_assert_out(stdout, stderr, mss_equal)
339
340
cmd = pre + 'lower_than_mss'
341
_, stdout, stderr = ssh_connection.execute_command(cmd)
342
_assert_out(stdout, stderr, lower_than_mss)
343
344
345
def test_mmds_dummy(test_microvm_with_ssh):
346
"""Test the API and guest facing features of the Micro MetaData Service."""
347
test_microvm = test_microvm_with_ssh
348
test_microvm.spawn()
349
350
# The MMDS is empty at this point.
351
response = test_microvm.mmds.get()
352
assert test_microvm.api_session.is_status_ok(response.status_code)
353
assert response.json() == {}
354
355
# Test that patch return NotInitialized when the MMDS is not initialized.
356
dummy_json = {
357
'latest': {
358
'meta-data': {
359
'ami-id': 'dummy'
360
}
361
}
362
}
363
response = test_microvm.mmds.patch(json=dummy_json)
364
assert test_microvm.api_session.is_status_bad_request(response.status_code)
365
fault_json = {
366
"fault_message": "The MMDS data store is not initialized."
367
}
368
assert response.json() == fault_json
369
370
# Test that using the same json with a PUT request, the MMDS data-store is
371
# created.
372
response = test_microvm.mmds.put(json=dummy_json)
373
assert test_microvm.api_session.is_status_no_content(response.status_code)
374
375
response = test_microvm.mmds.get()
376
assert test_microvm.api_session.is_status_ok(response.status_code)
377
assert response.json() == dummy_json
378
379
response = test_microvm.mmds.get()
380
assert test_microvm.api_session.is_status_ok(response.status_code)
381
assert response.json() == dummy_json
382
383
dummy_json = {
384
'latest': {
385
'meta-data': {
386
'ami-id': 'another_dummy',
387
'secret_key': 'eaasda48141411aeaeae'
388
}
389
}
390
}
391
response = test_microvm.mmds.patch(json=dummy_json)
392
assert test_microvm.api_session.is_status_no_content(response.status_code)
393
response = test_microvm.mmds.get()
394
assert test_microvm.api_session.is_status_ok(response.status_code)
395
assert response.json() == dummy_json
396
397
398
def test_guest_mmds_hang(test_microvm_with_ssh, network_config):
399
"""Test the MMDS json response."""
400
test_microvm = test_microvm_with_ssh
401
test_microvm.spawn()
402
403
response = test_microvm.mmds.get()
404
assert test_microvm.api_session.is_status_ok(response.status_code)
405
assert response.json() == {}
406
407
data_store = {
408
'latest': {
409
'meta-data': {
410
'ami-id': 'ami-12345678'
411
}
412
}
413
}
414
response = test_microvm.mmds.put(json=data_store)
415
assert test_microvm.api_session.is_status_no_content(response.status_code)
416
417
response = test_microvm.mmds.get()
418
assert test_microvm.api_session.is_status_ok(response.status_code)
419
assert response.json() == data_store
420
421
test_microvm.basic_config(vcpu_count=1)
422
_tap = test_microvm.ssh_network_config(
423
network_config,
424
'1',
425
allow_mmds_requests=True
426
)
427
428
test_microvm.start()
429
ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config)
430
431
cmd = 'ip route add 169.254.169.254 dev eth0'
432
_, stdout, stderr = ssh_connection.execute_command(cmd)
433
_assert_out(stdout, stderr, '')
434
435
# Test for a GET request with a content length longer than
436
# the actual length of the body.
437
cmd = 'curl -m 2 -s'
438
cmd += ' -X GET'
439
cmd += ' -H "Content-Length: 100"'
440
cmd += ' -H "Accept: application/json"'
441
cmd += ' -d "some body"'
442
cmd += ' http://169.254.169.254/'
443
444
_, stdout, _ = ssh_connection.execute_command(cmd)
445
assert 'Invalid request' in stdout.read()
446
447
# Do the same for a PUT request.
448
cmd = 'curl -m 2 -s'
449
cmd += ' -X PUT'
450
cmd += ' -H "Content-Length: 100"'
451
cmd += ' -H "Accept: application/json"'
452
cmd += ' -d "some body"'
453
cmd += ' http://169.254.169.254/'
454
455
_, stdout, _ = ssh_connection.execute_command(cmd)
456
assert 'Invalid request' in stdout.read()
457
458