Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/firecracker
Path: blob/main/tests/framework/resources.py
1956 views
1
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
# SPDX-License-Identifier: Apache-2.0
3
"""Defines classes for all the resources a microvm could need attaching."""
4
5
import urllib
6
7
from framework.defs import API_USOCKET_URL_PREFIX
8
9
10
class Actions():
11
"""Facility for sending operations instructions on the microvm."""
12
13
ACTIONS_CFG_RESOURCE = 'actions'
14
15
def __init__(self, api_usocket_full_name, api_session):
16
"""Specify the information needed for sending API requests."""
17
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
18
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
19
20
self._actions_cfg_url = api_url + self.ACTIONS_CFG_RESOURCE
21
self._api_session = api_session
22
23
def put(self, **args):
24
"""Send an instruction to the microvm."""
25
datax = self.create_json(**args)
26
return self._api_session.put(
27
"{}".format(self._actions_cfg_url),
28
json=datax
29
)
30
31
@staticmethod
32
def create_json(action_type=None, payload=None):
33
"""Compose the json associated to this type of API request."""
34
datax = {}
35
36
if action_type is not None:
37
datax['action_type'] = action_type
38
39
if payload is not None:
40
datax['payload'] = payload
41
42
return datax
43
44
45
class Balloon():
46
"""Facility for specifying balloon device configurations."""
47
48
BALLOON_CFG_RESOURCE = 'balloon'
49
50
def __init__(self, api_usocket_full_name, api_session):
51
"""Specify the information needed for sending API requests."""
52
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
53
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
54
55
self._balloon_cfg_url = api_url + self.BALLOON_CFG_RESOURCE
56
self._api_session = api_session
57
58
def put(self, **args):
59
"""Specify the balloon device configuration."""
60
datax = self.create_json(**args)
61
return self._api_session.put(
62
"{}".format(self._balloon_cfg_url),
63
json=datax
64
)
65
66
def patch(self, **args):
67
"""Update a previously attached balloon device."""
68
datax = self.create_json(**args)
69
return self._api_session.patch(
70
"{}".format(self._balloon_cfg_url),
71
json=datax
72
)
73
74
def patch_stats(self, **args):
75
"""Update the balloon statistics interval."""
76
datax = self.create_json(**args)
77
return self._api_session.patch(
78
"{}".format(self._balloon_cfg_url + "/statistics"),
79
json=datax
80
)
81
82
def get(self):
83
"""Get the response of specifying the balloon configuration."""
84
return self._api_session.get(
85
self._balloon_cfg_url
86
)
87
88
def get_stats(self):
89
"""Get the response of specifying the balloon statistics."""
90
return self._api_session.get(
91
"{}".format(self._balloon_cfg_url + "/statistics")
92
)
93
94
@staticmethod
95
def create_json(
96
amount_mib=None,
97
deflate_on_oom=None,
98
stats_polling_interval_s=None
99
):
100
"""Compose the json associated to this type of API request."""
101
datax = {}
102
103
if amount_mib is not None:
104
datax['amount_mib'] = amount_mib
105
106
if deflate_on_oom is not None:
107
datax['deflate_on_oom'] = deflate_on_oom
108
109
if stats_polling_interval_s is not None:
110
datax['stats_polling_interval_s'] = stats_polling_interval_s
111
112
return datax
113
114
115
class BootSource():
116
"""Facility for specifying the source of the boot process."""
117
118
BOOT_CFG_RESOURCE = 'boot-source'
119
120
def __init__(self, api_usocket_full_name, api_session):
121
"""Specify the information needed for sending API requests."""
122
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
123
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
124
125
self._boot_cfg_url = api_url + self.BOOT_CFG_RESOURCE
126
self._api_session = api_session
127
128
def put(self, **args):
129
"""Specify the boot information."""
130
datax = self.create_json(**args)
131
132
return self._api_session.put(
133
"{}".format(self._boot_cfg_url),
134
json=datax
135
)
136
137
def patch(self, **args):
138
"""Update a previously attached boot source."""
139
datax = self.create_json(**args)
140
return self._api_session.patch(
141
"{}".format(self._boot_cfg_url),
142
json=datax
143
)
144
145
def get(self):
146
"""Get the response of specifying a boot source."""
147
return self._api_session.get(
148
self._boot_cfg_url
149
)
150
151
@staticmethod
152
def create_json(
153
boot_args=None,
154
kernel_image_path=None,
155
initrd_path=None):
156
"""Compose the json associated to this type of API request."""
157
datax = {}
158
159
if kernel_image_path is not None:
160
datax['kernel_image_path'] = kernel_image_path
161
162
if initrd_path is not None:
163
datax['initrd_path'] = initrd_path
164
165
if boot_args is not None:
166
datax['boot_args'] = boot_args
167
168
return datax
169
170
171
# Too few public methods (1/2) (too-few-public-methods)
172
# pylint: disable=R0903
173
class DescribeInstance():
174
"""Facility for getting the microVM state."""
175
176
def __init__(self, api_usocket_full_name, api_session):
177
"""Specify the information needed for sending API requests."""
178
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
179
self._descinst_cfg_url = \
180
API_USOCKET_URL_PREFIX + url_encoded_path + '/'
181
self._api_session = api_session
182
183
def get(self):
184
"""Get the status of configuring the current microvm."""
185
return self._api_session.get(
186
self._descinst_cfg_url
187
)
188
189
190
class Drive():
191
"""Facility for attaching a block device."""
192
193
DRIVE_CFG_RESOURCE = 'drives'
194
195
def __init__(self, api_usocket_full_name, api_session):
196
"""Specify the information needed for sending API requests."""
197
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
198
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
199
200
self._drive_cfg_url = api_url + self.DRIVE_CFG_RESOURCE
201
self._api_session = api_session
202
203
def put(self, **args):
204
"""Attach a block device or update the details of a previous one."""
205
datax = self.create_json(**args)
206
207
return self._api_session.put(
208
"{}/{}".format(self._drive_cfg_url, args['drive_id']),
209
json=datax
210
)
211
212
def patch(self, **args):
213
"""Attach a block device or update the details of a previous one."""
214
datax = self.create_json(**args)
215
216
return self._api_session.patch(
217
"{}/{}".format(self._drive_cfg_url, args['drive_id']),
218
json=datax
219
)
220
221
def get(self, drive_id):
222
"""Get the status of attaching some block device."""
223
return self._api_session.get(
224
"{}/{}".format(self._drive_cfg_url, drive_id)
225
)
226
227
@staticmethod
228
def create_json(
229
drive_id=None,
230
path_on_host=None,
231
is_root_device=None,
232
partuuid=None,
233
is_read_only=None,
234
rate_limiter=None,
235
cache_type=None):
236
"""Compose the json associated to this type of API request."""
237
datax = {}
238
239
if drive_id is not None:
240
datax['drive_id'] = drive_id
241
242
if path_on_host is not None:
243
datax['path_on_host'] = path_on_host
244
245
if is_root_device is not None:
246
datax['is_root_device'] = is_root_device
247
248
if partuuid is not None:
249
datax['partuuid'] = partuuid
250
251
if is_read_only is not None:
252
datax['is_read_only'] = is_read_only
253
254
if cache_type is not None:
255
datax['cache_type'] = cache_type
256
257
if rate_limiter is not None:
258
datax['rate_limiter'] = rate_limiter
259
260
return datax
261
262
263
# Too few public methods (1/2) (too-few-public-methods)
264
# pylint: disable=R0903
265
class FullConfig():
266
"""Facility for getting the full microVM configuration."""
267
268
EXPORT_CFG_RESOURCE = 'vm/config'
269
270
def __init__(self, api_usocket_full_name, api_session):
271
"""Specify the information needed for sending API requests."""
272
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
273
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
274
275
self._export_cfg_url = api_url + self.EXPORT_CFG_RESOURCE
276
self._api_session = api_session
277
278
def get(self):
279
"""Get full configuration of the current microvm."""
280
return self._api_session.get(
281
self._export_cfg_url
282
)
283
284
285
class Logger():
286
"""Facility for setting up the logging system and sending API requests."""
287
288
LOGGER_CFG_RESOURCE = 'logger'
289
290
def __init__(self, api_usocket_full_name, api_session):
291
"""Specify the information needed for sending API requests."""
292
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
293
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
294
295
self._logger_cfg_url = api_url + self.LOGGER_CFG_RESOURCE
296
self._api_session = api_session
297
298
def put(self, **args):
299
"""Configure or update the settings of the logging system."""
300
datax = self.create_json(**args)
301
302
return self._api_session.put(
303
"{}".format(self._logger_cfg_url),
304
json=datax
305
)
306
307
def patch(self, **args):
308
"""Configure or update the settings of the logging system."""
309
datax = self.create_json(**args)
310
return self._api_session.patch(
311
"{}".format(self._logger_cfg_url),
312
json=datax
313
)
314
315
@staticmethod
316
def create_json(
317
log_path=None,
318
level=None,
319
show_level=None,
320
show_log_origin=None):
321
"""Compose the json associated to this type of API request."""
322
datax = {}
323
324
if log_path is not None:
325
datax['log_path'] = log_path
326
327
if level is not None:
328
datax['level'] = level
329
330
if show_level is not None:
331
datax['show_level'] = show_level
332
333
if show_log_origin is not None:
334
datax['show_log_origin'] = show_log_origin
335
336
return datax
337
338
339
class SnapshotCreate():
340
"""Facility for sending create snapshot commands on the microvm."""
341
342
SNAPSHOT_CREATE_URL = 'snapshot/create'
343
344
def __init__(self, api_usocket_full_name, api_session):
345
"""Specify the information needed for sending API requests."""
346
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
347
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
348
self._snapshot_cfg_url = api_url + self.SNAPSHOT_CREATE_URL
349
self._api_session = api_session
350
351
def put(self, **args):
352
"""Create a snapshot of the microvm."""
353
self._api_session.untime()
354
datax = self.create_json(**args)
355
return self._api_session.put(
356
"{}".format(self._snapshot_cfg_url),
357
json=datax
358
)
359
360
@staticmethod
361
def create_json(mem_file_path, snapshot_path, diff=False, version=None):
362
"""Compose the json associated to this type of API request."""
363
if diff:
364
snapshot_type = 'Diff'
365
else:
366
snapshot_type = 'Full'
367
datax = {
368
'mem_file_path': mem_file_path,
369
'snapshot_path': snapshot_path,
370
'snapshot_type': snapshot_type,
371
}
372
if version is not None:
373
datax['version'] = version
374
375
return datax
376
377
378
class SnapshotLoad():
379
"""Facility for sending load snapshot commands on the microvm."""
380
381
SNAPSHOT_LOAD_URL = 'snapshot/load'
382
383
def __init__(self, api_usocket_full_name, api_session):
384
"""Specify the information needed for sending API requests."""
385
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
386
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
387
self._snapshot_cfg_url = api_url + self.SNAPSHOT_LOAD_URL
388
self._api_session = api_session
389
390
def put(self, **args):
391
"""Load a snapshot of the microvm."""
392
datax = self.create_json(**args)
393
return self._api_session.put(
394
"{}".format(self._snapshot_cfg_url),
395
json=datax
396
)
397
398
@staticmethod
399
def create_json(mem_file_path, snapshot_path, diff=False, resume=False):
400
"""Compose the json associated to this type of API request."""
401
datax = {
402
'mem_file_path': mem_file_path,
403
'snapshot_path': snapshot_path,
404
}
405
if diff:
406
datax['enable_diff_snapshots'] = True
407
if resume:
408
datax['resume_vm'] = True
409
return datax
410
411
412
class SnapshotHelper():
413
"""Facility for creation and loading of microvm snapshots."""
414
415
def __init__(self, api_usocket_full_name, api_session):
416
"""Specify the information needed for sending API requests."""
417
self._create = SnapshotCreate(api_usocket_full_name, api_session)
418
self._load = SnapshotLoad(api_usocket_full_name, api_session)
419
self._vm_state = Vm(api_usocket_full_name, api_session)
420
421
def create(self, mem_file_path, snapshot_path, diff=False, version=None):
422
"""Create a snapshot of the microvm."""
423
return self._create.put(
424
mem_file_path=mem_file_path,
425
snapshot_path=snapshot_path,
426
diff=diff,
427
version=version
428
)
429
430
def load(self, mem_file_path, snapshot_path, diff=False, resume=False):
431
"""Load a snapshot of the microvm."""
432
response = self._load.put(
433
mem_file_path=mem_file_path,
434
snapshot_path=snapshot_path,
435
diff=diff,
436
resume=resume
437
)
438
439
if resume and "unknown field `resume_vm`" in response.text:
440
# Retry using old API - separate resume command.
441
response = self._load.put(
442
mem_file_path=mem_file_path,
443
snapshot_path=snapshot_path,
444
diff=diff,
445
resume=False
446
)
447
if response.status_code != 204:
448
return response
449
response = self._vm_state.patch(state='Resumed')
450
451
return response
452
453
454
class Metrics:
455
"""Facility for setting up the metrics system and sending API requests."""
456
457
METRICS_CFG_RESOURCE = 'metrics'
458
459
def __init__(self, api_usocket_full_name, api_session):
460
"""Specify the information needed for sending API requests."""
461
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
462
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
463
464
self._metrics_cfg_url = api_url + self.METRICS_CFG_RESOURCE
465
self._api_session = api_session
466
467
def put(self, **args):
468
"""Configure or update the settings of the metrics system."""
469
datax = self.create_json(**args)
470
return self._api_session.put(
471
"{}".format(self._metrics_cfg_url),
472
json=datax
473
)
474
475
def patch(self, **args):
476
"""Configure or update the settings of the metrics system."""
477
datax = self.create_json(**args)
478
return self._api_session.patch(
479
"{}".format(self._metrics_cfg_url),
480
json=datax
481
)
482
483
@staticmethod
484
def create_json(
485
metrics_path=None,
486
):
487
"""Compose the json associated to this type of API request."""
488
datax = {}
489
if metrics_path is not None:
490
datax['metrics_path'] = metrics_path
491
return datax
492
493
494
class MachineConfigure():
495
"""Facility for configuring the machine capabilities."""
496
497
MACHINE_CFG_RESOURCE = 'machine-config'
498
499
def __init__(self, api_usocket_full_name, api_session):
500
"""Specify the information needed for sending API requests."""
501
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
502
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
503
504
self._machine_cfg_url = api_url + self.MACHINE_CFG_RESOURCE
505
self._api_session = api_session
506
self._datax = {}
507
508
@property
509
def configuration(self):
510
"""Return machine config dictionary."""
511
return self._datax
512
513
def put(self, **args):
514
"""Specify the details of the machine configuration."""
515
self._datax = self.create_json(**args)
516
517
return self._api_session.put(
518
"{}".format(self._machine_cfg_url),
519
json=self._datax
520
)
521
522
def patch(self, **args):
523
"""Update the details of the machine configuration."""
524
datax = self.create_json(**args)
525
self._datax.update(datax)
526
527
return self._api_session.patch(
528
"{}".format(self._machine_cfg_url),
529
json=datax
530
)
531
532
def get(self):
533
"""Get the status of configuring the current microvm."""
534
return self._api_session.get(
535
self._machine_cfg_url
536
)
537
538
@staticmethod
539
def create_json(
540
vcpu_count=None,
541
mem_size_mib=None,
542
ht_enabled=None,
543
cpu_template=None,
544
track_dirty_pages=None):
545
"""Compose the json associated to this type of API request."""
546
datax = {}
547
if vcpu_count is not None:
548
datax['vcpu_count'] = vcpu_count
549
550
if mem_size_mib is not None:
551
datax['mem_size_mib'] = mem_size_mib
552
553
if ht_enabled is not None:
554
datax['ht_enabled'] = ht_enabled
555
556
if cpu_template is not None:
557
datax['cpu_template'] = cpu_template
558
559
if track_dirty_pages is not None:
560
datax['track_dirty_pages'] = track_dirty_pages
561
562
return datax
563
564
565
class MMDS():
566
"""Facility for sending microvm metadata services related API calls."""
567
568
MMDS_CFG_RESOURCE = 'mmds'
569
570
def __init__(self, api_usocket_full_name, api_session):
571
"""Specify the information needed for sending MMDS API requests."""
572
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
573
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
574
575
self._mmds_cfg_url = api_url + self.MMDS_CFG_RESOURCE
576
self._api_session = api_session
577
578
def put(self, **args):
579
"""Send a new MMDS request."""
580
return self._api_session.put(
581
"{}".format(self._mmds_cfg_url),
582
json=args['json']
583
)
584
585
def put_config(self, **args):
586
"""Send a new MMDS config request."""
587
return self._api_session.put(
588
"{}".format(self._mmds_cfg_url + "/config"),
589
json=args['json']
590
)
591
592
def patch(self, **args):
593
"""Update the details of some MMDS request."""
594
return self._api_session.patch(
595
"{}".format(self._mmds_cfg_url),
596
json=args['json']
597
)
598
599
def get(self):
600
"""Get the status of the mmds request."""
601
return self._api_session.get(
602
self._mmds_cfg_url
603
)
604
605
606
class Network():
607
"""Facility for handling network configuration for a microvm."""
608
609
NET_CFG_RESOURCE = 'network-interfaces'
610
611
def __init__(self, api_usocket_full_name, api_session):
612
"""Specify the information needed for sending API requests."""
613
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
614
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
615
616
self._net_cfg_url = api_url + self.NET_CFG_RESOURCE
617
self._api_session = api_session
618
619
def put(self, **args):
620
"""Attach a new tap interface."""
621
datax = self.create_json(**args)
622
623
return self._api_session.put(
624
"{}/{}".format(self._net_cfg_url, args['iface_id']),
625
json=datax
626
)
627
628
def patch(self, **args):
629
"""Apply an update to some tap interface."""
630
datax = self.create_json(**args)
631
632
return self._api_session.patch(
633
"{}/{}".format(self._net_cfg_url, args['iface_id']),
634
json=datax
635
)
636
637
@staticmethod
638
def create_json(
639
iface_id=None,
640
host_dev_name=None,
641
guest_mac=None,
642
allow_mmds_requests=None,
643
rx_rate_limiter=None,
644
tx_rate_limiter=None):
645
"""Create the json for the net specific API request."""
646
datax = {
647
'iface_id': iface_id
648
}
649
650
if host_dev_name is not None:
651
datax['host_dev_name'] = host_dev_name
652
653
if guest_mac is not None:
654
datax['guest_mac'] = guest_mac
655
656
if allow_mmds_requests is not None:
657
datax['allow_mmds_requests'] = allow_mmds_requests
658
659
if tx_rate_limiter is not None:
660
datax['tx_rate_limiter'] = tx_rate_limiter
661
662
if rx_rate_limiter is not None:
663
datax['rx_rate_limiter'] = rx_rate_limiter
664
665
return datax
666
667
668
class Vm():
669
"""Facility for handling the state for a microvm."""
670
671
VM_CFG_RESOURCE = 'vm'
672
673
def __init__(self, api_usocket_full_name, api_session):
674
"""Specify the information needed for sending API requests."""
675
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
676
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
677
678
self._vm_cfg_url = api_url + self.VM_CFG_RESOURCE
679
self._api_session = api_session
680
681
def patch(self, **args):
682
"""Apply an update to the microvm state."""
683
datax = self.create_json(**args)
684
685
return self._api_session.patch(
686
self._vm_cfg_url,
687
json=datax
688
)
689
690
@staticmethod
691
def create_json(state):
692
"""Create the json for the vm specific API request."""
693
datax = {
694
'state': state
695
}
696
697
return datax
698
699
700
class Vsock():
701
"""Facility for handling vsock configuration for a microvm."""
702
703
VSOCK_CFG_RESOURCE = 'vsock'
704
705
def __init__(self, api_usocket_full_name, api_session):
706
"""Specify the information needed for sending API requests."""
707
url_encoded_path = urllib.parse.quote_plus(api_usocket_full_name)
708
api_url = API_USOCKET_URL_PREFIX + url_encoded_path + '/'
709
710
self._vsock_cfg_url = api_url + self.VSOCK_CFG_RESOURCE
711
self._api_session = api_session
712
713
def put(self, **args):
714
"""Attach a new vsock device."""
715
datax = self.create_json(**args)
716
717
return self._api_session.put(
718
self._vsock_cfg_url,
719
json=datax
720
)
721
722
def patch(self, **args):
723
"""Apply an update to some vsock device."""
724
datax = self.create_json(**args)
725
726
return self._api_session.patch(
727
self._vsock_cfg_url,
728
json=datax
729
)
730
731
@staticmethod
732
def create_json(
733
vsock_id,
734
guest_cid,
735
uds_path):
736
"""Create the json for the vsock specific API request."""
737
datax = {
738
'vsock_id': vsock_id,
739
'guest_cid': guest_cid,
740
'uds_path': uds_path
741
}
742
743
return datax
744
745