9
9
import time
10
10
import argparse
11
11
import re
12
+ import json
13
+ import tempfile
12
14
from datetime import datetime
13
- import requests_unixsocket
14
15
15
16
16
17
verbose = False
@@ -21,9 +22,16 @@ class ApiException(Exception):
21
22
22
23
23
24
class ApiClient (object ):
24
- def __init__ (self , domain_socket_path ):
25
- self .socket_path = domain_socket_path
26
- self .session = requests_unixsocket .Session ()
25
+ def __init__ (self , domain_socket_path = None ):
26
+ if domain_socket_path != None :
27
+ import requests_unixsocket
28
+ self .socket_less = False
29
+ self .socket_path = domain_socket_path
30
+ self .session = requests_unixsocket .Session ()
31
+ else :
32
+ self .socket_less = True
33
+ self .firecracker_config = {}
34
+ print_time ("API socket-less: %s" % self .socket_less )
27
35
28
36
def api_socket_url (self , path ):
29
37
return "http+unix://%s%s" % (self .socket_path , path )
@@ -36,21 +44,29 @@ def make_put_call(self, path, request_body):
36
44
return res .status_code
37
45
38
46
def create_instance (self , kernel_image_path , cmdline ):
39
- self . make_put_call ( '/boot-source' , {
47
+ boot_source = {
40
48
'kernel_image_path' : kernel_image_path ,
41
49
'boot_args' : cmdline
42
- })
50
+ }
51
+ if self .socket_less :
52
+ self .firecracker_config ['boot-source' ] = boot_source
53
+ else :
54
+ self .make_put_call ('/boot-source' , boot_source )
43
55
44
56
def add_disk (self , disk_image_path ):
45
- self . make_put_call ( '/drives/rootfs' , {
57
+ drive = {
46
58
'drive_id' : 'rootfs' ,
47
59
'path_on_host' : disk_image_path ,
48
60
'is_root_device' : False ,
49
61
'is_read_only' : False
50
- })
62
+ }
63
+ if self .socket_less :
64
+ self .firecracker_config ['drives' ] = [drive ]
65
+ else :
66
+ self .make_put_call ('/drives/rootfs' , drive )
51
67
52
- def add_network_interface (self , interface_name , host_interface_name , ):
53
- self . make_put_call ( '/network-interfaces/%s' % interface_name , {
68
+ def add_network_interface (self , interface_name , host_interface_name ):
69
+ interface = {
54
70
'iface_id' : interface_name ,
55
71
'host_dev_name' : host_interface_name ,
56
72
'guest_mac' : "52:54:00:12:34:56" ,
@@ -74,29 +90,44 @@ def add_network_interface(self, interface_name, host_interface_name, ):
74
90
'refill_time' : 0
75
91
}
76
92
}
77
- })
93
+ }
94
+ if self .socket_less :
95
+ self .firecracker_config ['network-interfaces' ] = [interface ]
96
+ else :
97
+ self .make_put_call ('/network-interfaces/%s' % interface_name , interface )
78
98
79
99
def start_instance (self ):
80
- self .make_put_call ('/actions' , {
81
- 'action_type' : 'InstanceStart'
82
- })
100
+ if self .socket_less == False :
101
+ self .make_put_call ('/actions' , {
102
+ 'action_type' : 'InstanceStart'
103
+ })
83
104
84
105
def configure_logging (self ):
85
- self . make_put_call ( '/logger' , {
106
+ log_config = {
86
107
"log_fifo" : "log.fifo" ,
87
108
"metrics_fifo" : "metrics.fifo" ,
88
109
"level" : "Info" ,
89
110
"show_level" : True ,
90
111
"show_log_origin" : True
91
- })
112
+ }
113
+ if self .socket_less :
114
+ self .firecracker_config ['logger' ] = log_config
115
+ else :
116
+ self .make_put_call ('/logger' , log_config )
92
117
93
118
def configure_machine (self , vcpu_count , mem_size_in_mb ):
94
- self . make_put_call ( '/machine-config' , {
119
+ machine_config = {
95
120
'vcpu_count' : vcpu_count ,
96
121
'mem_size_mib' : mem_size_in_mb ,
97
122
'ht_enabled' : False
98
- })
123
+ }
124
+ if self .socket_less :
125
+ self .firecracker_config ['machine-config' ] = machine_config
126
+ else :
127
+ self .make_put_call ('/machine-config' , machine_config )
99
128
129
+ def firecracker_config_json (self ):
130
+ return json .dumps (self .firecracker_config , indent = 3 )
100
131
101
132
def print_time (msg ):
102
133
if verbose :
@@ -134,7 +165,7 @@ def find_firecracker(dirname):
134
165
firecracker_path = os .environ .get ('FIRECRACKER_PATH' )
135
166
136
167
# And offer to install if not found
137
- firecracker_version = 'v0.18 .0'
168
+ firecracker_version = 'v0.19 .0'
138
169
if not os .path .exists (firecracker_path ):
139
170
url_base = 'https://github.com/firecracker-microvm/firecracker/releases/download'
140
171
download_url = '%s/%s/firecracker-%s' % (url_base , firecracker_version , firecracker_version )
@@ -185,6 +216,13 @@ def start_firecracker(firecracker_path, socket_path):
185
216
return subprocess .Popen ([firecracker_path , '--api-sock' , socket_path ],
186
217
stdout = sys .stdout , stderr = subprocess .STDOUT )
187
218
219
+ def start_firecracker_with_no_api (firecracker_path , firecracker_config_json ):
220
+ # Start firecracker process and pass configuration JSON as a file
221
+ api_file = tempfile .NamedTemporaryFile (delete = False )
222
+ api_file .write (firecracker_config_json )
223
+ return subprocess .Popen ([firecracker_path , "--no-api" , "--config-file" , api_file .name ],
224
+ stdout = sys .stdout , stderr = subprocess .STDOUT ), api_file .name
225
+
188
226
189
227
def get_memory_size_in_mb (options ):
190
228
memory_in_mb = 128
@@ -206,16 +244,17 @@ def main(options):
206
244
# Firecracker is installed so lets start
207
245
print_time ("Start" )
208
246
socket_path = '/tmp/firecracker.socket'
209
- firecracker = start_firecracker (firecracker_path , socket_path )
247
+ if not options .api_less :
248
+ firecracker = start_firecracker (firecracker_path , socket_path )
210
249
211
250
# Prepare arguments we are going to pass when creating VM instance
212
251
kernel_path = options .kernel
213
252
if not kernel_path :
214
- kernel_path = os .path .join (dirname , '../build/release/loader-stripped.elf' )
253
+ kernel_path = os .path .join (dirname , '../build/release/loader-stripped.elf' )
215
254
216
255
qemu_disk_path = options .image
217
256
if not qemu_disk_path :
218
- qemu_disk_path = os .path .join (dirname , '../build/release/usr.img' )
257
+ qemu_disk_path = os .path .join (dirname , '../build/release/usr.img' )
219
258
raw_disk_path = disk_path (qemu_disk_path )
220
259
221
260
cmdline = options .execute
@@ -235,15 +274,19 @@ def main(options):
235
274
cmdline = '--verbose ' + cmdline
236
275
237
276
# Create API client and make API calls
238
- client = ApiClient (socket_path .replace ("/" , "%2F" ))
277
+ if options .api_less :
278
+ client = ApiClient ()
279
+ else :
280
+ client = ApiClient (socket_path .replace ("/" , "%2F" ))
239
281
240
282
try :
241
283
# Very often on the very first run firecracker process
242
284
# is not ready yet to accept calls over socket file
243
285
# so we poll existence of this file as a good
244
286
# enough indicator if firecracker is ready
245
- while not os .path .exists (socket_path ):
246
- time .sleep (0.01 )
287
+ if not options .api_less :
288
+ while not os .path .exists (socket_path ):
289
+ time .sleep (0.01 )
247
290
print_time ("Firecracker ready" )
248
291
249
292
memory_in_mb = get_memory_size_in_mb (options )
@@ -259,8 +302,11 @@ def main(options):
259
302
client .create_instance (kernel_path , cmdline )
260
303
print_time ("Created OSv VM with cmdline: %s" % cmdline )
261
304
262
- client .start_instance ()
263
- print_time ("Booted OSv VM" )
305
+ if options .api_less :
306
+ firecracker , config_file_path = start_firecracker_with_no_api (firecracker_path , client .firecracker_config_json ())
307
+ else :
308
+ client .start_instance ()
309
+ print_time ("Booted OSv VM" )
264
310
265
311
except ApiException as e :
266
312
print ("Failed to make firecracker API call: %s." % e )
@@ -274,6 +320,8 @@ def main(options):
274
320
275
321
print_time ("Waiting for firecracker process to terminate" )
276
322
firecracker .wait ()
323
+ if options .api_less :
324
+ os .unlink (config_file_path )
277
325
print_time ("End" )
278
326
279
327
@@ -296,6 +344,8 @@ def main(options):
296
344
help = "bridge name for tap networking" )
297
345
parser .add_argument ("-V" , "--verbose" , action = "store_true" ,
298
346
help = "pass --verbose to OSv, to display more debugging information on the console" )
347
+ parser .add_argument ("-l" , "--api_less" , action = "store_true" ,
348
+ help = "do NOT use socket-based API to configure and start OSv on firecracker" )
299
349
300
350
cmd_args = parser .parse_args ()
301
351
if cmd_args .verbose :
0 commit comments