Skip to content

Commit a28717b

Browse files
committed
firecracker: upgrade to the latest version 0.19.0 and enhance script to use api-less mode
This patch enhances firecracker.py to allow starting firecracker in "api-less" mode. In the api-less mode firecracker takes parameters from config file instead of listening on domain socket that needs to be created beforehand. This way firecracker starts faster by 60-90 ms. Signed-off-by: Waldemar Kozaczuk <[email protected]>
1 parent 10afbb4 commit a28717b

File tree

1 file changed

+77
-27
lines changed

1 file changed

+77
-27
lines changed

scripts/firecracker.py

Lines changed: 77 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
import time
1010
import argparse
1111
import re
12+
import json
13+
import tempfile
1214
from datetime import datetime
13-
import requests_unixsocket
1415

1516

1617
verbose = False
@@ -21,9 +22,16 @@ class ApiException(Exception):
2122

2223

2324
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)
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

101132
def 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

189227
def 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

Comments
 (0)