99import time
1010import argparse
1111import re
12+ import json
13+ import tempfile
1214from datetime import datetime
13- import requests_unixsocket
1415
1516
1617verbose = False
@@ -21,9 +22,16 @@ class ApiException(Exception):
2122
2223
2324class 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 )
2735
2836 def api_socket_url (self , path ):
2937 return "http+unix://%s%s" % (self .socket_path , path )
@@ -36,21 +44,29 @@ def make_put_call(self, path, request_body):
3644 return res .status_code
3745
3846 def create_instance (self , kernel_image_path , cmdline ):
39- self . make_put_call ( '/boot-source' , {
47+ boot_source = {
4048 'kernel_image_path' : kernel_image_path ,
4149 '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 )
4355
4456 def add_disk (self , disk_image_path ):
45- self . make_put_call ( '/drives/rootfs' , {
57+ drive = {
4658 'drive_id' : 'rootfs' ,
4759 'path_on_host' : disk_image_path ,
4860 'is_root_device' : False ,
4961 '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 )
5167
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 = {
5470 'iface_id' : interface_name ,
5571 'host_dev_name' : host_interface_name ,
5672 'guest_mac' : "52:54:00:12:34:56" ,
@@ -74,29 +90,44 @@ def add_network_interface(self, interface_name, host_interface_name, ):
7490 'refill_time' : 0
7591 }
7692 }
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 )
7898
7999 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+ })
83104
84105 def configure_logging (self ):
85- self . make_put_call ( '/logger' , {
106+ log_config = {
86107 "log_fifo" : "log.fifo" ,
87108 "metrics_fifo" : "metrics.fifo" ,
88109 "level" : "Info" ,
89110 "show_level" : True ,
90111 "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 )
92117
93118 def configure_machine (self , vcpu_count , mem_size_in_mb ):
94- self . make_put_call ( '/machine-config' , {
119+ machine_config = {
95120 'vcpu_count' : vcpu_count ,
96121 'mem_size_mib' : mem_size_in_mb ,
97122 '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 )
99128
129+ def firecracker_config_json (self ):
130+ return json .dumps (self .firecracker_config , indent = 3 )
100131
101132def print_time (msg ):
102133 if verbose :
@@ -134,7 +165,7 @@ def find_firecracker(dirname):
134165 firecracker_path = os .environ .get ('FIRECRACKER_PATH' )
135166
136167 # And offer to install if not found
137- firecracker_version = 'v0.18 .0'
168+ firecracker_version = 'v0.19 .0'
138169 if not os .path .exists (firecracker_path ):
139170 url_base = 'https://github.com/firecracker-microvm/firecracker/releases/download'
140171 download_url = '%s/%s/firecracker-%s' % (url_base , firecracker_version , firecracker_version )
@@ -185,6 +216,13 @@ def start_firecracker(firecracker_path, socket_path):
185216 return subprocess .Popen ([firecracker_path , '--api-sock' , socket_path ],
186217 stdout = sys .stdout , stderr = subprocess .STDOUT )
187218
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+
188226
189227def get_memory_size_in_mb (options ):
190228 memory_in_mb = 128
@@ -206,16 +244,17 @@ def main(options):
206244 # Firecracker is installed so lets start
207245 print_time ("Start" )
208246 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 )
210249
211250 # Prepare arguments we are going to pass when creating VM instance
212251 kernel_path = options .kernel
213252 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' )
215254
216255 qemu_disk_path = options .image
217256 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' )
219258 raw_disk_path = disk_path (qemu_disk_path )
220259
221260 cmdline = options .execute
@@ -235,15 +274,19 @@ def main(options):
235274 cmdline = '--verbose ' + cmdline
236275
237276 # 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" ))
239281
240282 try :
241283 # Very often on the very first run firecracker process
242284 # is not ready yet to accept calls over socket file
243285 # so we poll existence of this file as a good
244286 # 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 )
247290 print_time ("Firecracker ready" )
248291
249292 memory_in_mb = get_memory_size_in_mb (options )
@@ -259,8 +302,11 @@ def main(options):
259302 client .create_instance (kernel_path , cmdline )
260303 print_time ("Created OSv VM with cmdline: %s" % cmdline )
261304
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" )
264310
265311 except ApiException as e :
266312 print ("Failed to make firecracker API call: %s." % e )
@@ -274,6 +320,8 @@ def main(options):
274320
275321 print_time ("Waiting for firecracker process to terminate" )
276322 firecracker .wait ()
323+ if options .api_less :
324+ os .unlink (config_file_path )
277325 print_time ("End" )
278326
279327
@@ -296,6 +344,8 @@ def main(options):
296344 help = "bridge name for tap networking" )
297345 parser .add_argument ("-V" , "--verbose" , action = "store_true" ,
298346 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" )
299349
300350 cmd_args = parser .parse_args ()
301351 if cmd_args .verbose :
0 commit comments