@@ -18,11 +18,15 @@ import (
18
18
"encoding/json"
19
19
"errors"
20
20
"fmt"
21
+ "io"
22
+ "io/ioutil"
21
23
"os"
24
+ "path/filepath"
22
25
"strconv"
23
26
"strings"
24
27
25
28
"github.com/firecracker-microvm/firecracker-go-sdk"
29
+ models "github.com/firecracker-microvm/firecracker-go-sdk/client/models"
26
30
"github.com/jessevdk/go-flags"
27
31
log "github.com/sirupsen/logrus"
28
32
)
@@ -43,28 +47,37 @@ const (
43
47
executableMask = 0111
44
48
)
45
49
46
- func parseBlockDevices (entries []string ) ([]firecracker.BlockDevice , error ) {
47
- var devices []firecracker.BlockDevice
48
- for _ , entry := range entries {
49
- var path string
50
+ func parseBlockDevices (entries []string ) ([]models.Drive , error ) {
51
+ devices := []models.Drive {}
52
+
53
+ for i , entry := range entries {
54
+ path := ""
55
+ readOnly := true
56
+
50
57
if strings .HasSuffix (entry , ":rw" ) {
58
+ readOnly = false
51
59
path = strings .TrimSuffix (entry , ":rw" )
52
60
} else if strings .HasSuffix (entry , ":ro" ) {
53
61
path = strings .TrimSuffix (entry , ":ro" )
54
62
} else {
55
63
msg := fmt .Sprintf ("Invalid drive specification. Must have :rw or :ro suffix" )
56
- return []firecracker. BlockDevice {} , errors .New (msg )
64
+ return nil , errors .New (msg )
57
65
}
66
+
58
67
if path == "" {
59
68
return nil , errors .New ("Invalid drive specification" )
60
69
}
61
- _ , err := os . Stat ( path )
62
- if err != nil {
70
+
71
+ if _ , err := os . Stat ( path ); err != nil {
63
72
return nil , err
64
73
}
65
- e := firecracker.BlockDevice {
66
- HostPath : path ,
67
- Mode : "rw" ,
74
+
75
+ e := models.Drive {
76
+ // i + 2 represents the drive ID. We will reserve 1 for root.
77
+ DriveID : firecracker .String (strconv .Itoa (i + 2 )),
78
+ PathOnHost : firecracker .String (path ),
79
+ IsReadOnly : firecracker .Bool (readOnly ),
80
+ IsRootDevice : firecracker .Bool (false ),
68
81
}
69
82
devices = append (devices , e )
70
83
}
@@ -105,6 +118,76 @@ func parseVsocks(devices []string) ([]firecracker.VsockDevice, error) {
105
118
return result , nil
106
119
}
107
120
121
+ func createFifoFileLogs (fifoPath string ) (* os.File , error ) {
122
+ return os .OpenFile (fifoPath , os .O_CREATE | os .O_APPEND | os .O_WRONLY , 0644 )
123
+ }
124
+
125
+ // handleFifos will see if any fifos need to be generated and if a fifo log
126
+ // file should be created.
127
+ func handleFifos (opts * options ) (io.Writer , []func () error , error ) {
128
+ // these booleans are used to check whether or not the fifo queue or metrics
129
+ // fifo queue needs to be generated. If any which need to be generated, then
130
+ // we know we need to create a temporary directory. Otherwise, a temporary
131
+ // directory does not need to be created.
132
+ generateFifoFilename := false
133
+ generateMetricFifoFilename := false
134
+ cleanupFns := []func () error {}
135
+ var err error
136
+
137
+ var fifo io.WriteCloser
138
+ if len (opts .FcFifoLogFile ) > 0 {
139
+ if len (opts .FcLogFifo ) > 0 {
140
+ return nil , cleanupFns , fmt .Errorf ("vmm-log-fifo and firecracker-log cannot be used together" )
141
+ }
142
+
143
+ generateFifoFilename = true
144
+ // if a fifo log file was specified via the CLI then we need to check if
145
+ // metric fifo was also specified. If not, we will then generate that fifo
146
+ if len (opts .FcMetricsFifo ) == 0 {
147
+ generateMetricFifoFilename = true
148
+ }
149
+
150
+ if fifo , err = createFifoFileLogs (opts .FcFifoLogFile ); err != nil {
151
+ return fifo , cleanupFns , fmt .Errorf ("Failed to create fifo log file: %v" , err )
152
+ }
153
+
154
+ cleanupFns = append (cleanupFns , func () error {
155
+ return fifo .Close ()
156
+ })
157
+ } else if len (opts .FcLogFifo ) > 0 || len (opts .FcMetricsFifo ) > 0 {
158
+ // this checks to see if either one of the fifos was set. If at least one
159
+ // has been set we check to see if any of the others were not set. If one
160
+ // isn't set, we will generate the proper file path.
161
+ if len (opts .FcLogFifo ) == 0 {
162
+ generateFifoFilename = true
163
+ }
164
+
165
+ if len (opts .FcMetricsFifo ) == 0 {
166
+ generateMetricFifoFilename = true
167
+ }
168
+ }
169
+
170
+ if generateFifoFilename || generateMetricFifoFilename {
171
+ dir , err := ioutil .TempDir (os .TempDir (), "fcfifo" )
172
+ if err != nil {
173
+ return fifo , cleanupFns , fmt .Errorf ("Fail to create temporary directory: %v" , err )
174
+ }
175
+
176
+ cleanupFns = append (cleanupFns , func () error {
177
+ return os .RemoveAll (dir )
178
+ })
179
+ if generateFifoFilename {
180
+ opts .FcLogFifo = filepath .Join (dir , "fc_fifo" )
181
+ }
182
+
183
+ if generateMetricFifoFilename {
184
+ opts .FcMetricsFifo = filepath .Join (dir , "fc_metrics_fifo" )
185
+ }
186
+ }
187
+
188
+ return fifo , cleanupFns , nil
189
+ }
190
+
108
191
type options struct {
109
192
FcBinary string `long:"firecracker-binary" description:"Path to firecracker binary"`
110
193
FcKernelImage string `long:"kernel" description:"Path to the kernel image" default:"./vmlinux"`
@@ -122,6 +205,7 @@ type options struct {
122
205
FcCPUTemplate string `long:"cpu-template" description:"Firecracker CPU Template (C3 or T2)"`
123
206
FcMemSz int64 `long:"memory" short:"m" description:"VM memory, in MiB" default:"512"`
124
207
FcMetadata string `long:"metadata" description:"Firecracker Meatadata for MMDS (json)"`
208
+ FcFifoLogFile string `long:"firecracker-log" short:"l" description:"pipes the fifo contents to the specified file"`
125
209
Debug bool `long:"debug" short:"d" description:"Enable debug output"`
126
210
Help bool `long:"help" short:"h" description:"Show usage"`
127
211
}
@@ -164,7 +248,7 @@ func main() {
164
248
if err != nil {
165
249
log .Fatalf ("Unable to parse NIC config: %s" , err )
166
250
} else {
167
- log .Printf ("Adding tap device %s" , tapDev )
251
+ log .Error ("Adding tap device %s" , tapDev )
168
252
allowMDDS := metadata != nil
169
253
NICs = []firecracker.NetworkInterface {
170
254
firecracker.NetworkInterface {
@@ -176,35 +260,57 @@ func main() {
176
260
}
177
261
}
178
262
179
- rootDrive := firecracker.BlockDevice {HostPath : opts .FcRootDrivePath , Mode : "rw" }
180
-
181
263
blockDevices , err := parseBlockDevices (opts .FcAdditionalDrives )
182
264
if err != nil {
183
265
log .Fatalf ("Invalid block device specification: %s" , err )
184
266
}
185
267
268
+ rootDrive := models.Drive {
269
+ DriveID : firecracker .String ("1" ),
270
+ PathOnHost : & opts .FcRootDrivePath ,
271
+ IsRootDevice : firecracker .Bool (true ),
272
+ IsReadOnly : firecracker .Bool (false ),
273
+ Partuuid : opts .FcRootPartUUID ,
274
+ }
275
+ blockDevices = append (blockDevices , rootDrive )
276
+
186
277
vsocks , err := parseVsocks (opts .FcVsockDevices )
187
278
if err != nil {
188
279
log .Fatalf ("Invalid vsock specification: %s" , err )
189
280
}
190
281
282
+ fifo , cleanFns , err := handleFifos (& opts )
283
+ // we call cleanup first due to errors returning at different points which
284
+ // may result in a file handle being opened.
285
+ defer func () {
286
+ for _ , fn := range cleanFns {
287
+ if err := fn (); err != nil {
288
+ log .WithError (err ).Error ("Failed to cleanup" )
289
+ }
290
+ }
291
+ }()
292
+ if err != nil {
293
+ log .Fatalf ("%v" , err )
294
+ }
295
+
191
296
fcCfg := firecracker.Config {
192
297
SocketPath : "./firecracker.sock" ,
193
298
LogFifo : opts .FcLogFifo ,
194
299
LogLevel : opts .FcLogLevel ,
195
300
MetricsFifo : opts .FcMetricsFifo ,
301
+ FifoLogWriter : fifo ,
196
302
KernelImagePath : opts .FcKernelImage ,
197
303
KernelArgs : opts .FcKernelCmdLine ,
198
- RootDrive : rootDrive ,
199
- RootPartitionUUID : opts .FcRootPartUUID ,
200
- AdditionalDrives : blockDevices ,
304
+ Drives : blockDevices ,
201
305
NetworkInterfaces : NICs ,
202
306
VsockDevices : vsocks ,
203
- CPUCount : opts .FcCPUCount ,
204
- CPUTemplate : firecracker .CPUTemplate (opts .FcCPUTemplate ),
205
- HtEnabled : ! opts .FcDisableHt ,
206
- MemInMiB : opts .FcMemSz ,
207
- Debug : opts .Debug ,
307
+ MachineCfg : models.MachineConfiguration {
308
+ VcpuCount : opts .FcCPUCount ,
309
+ CPUTemplate : models .CPUTemplate (opts .FcCPUTemplate ),
310
+ HtEnabled : ! opts .FcDisableHt ,
311
+ MemSizeMib : opts .FcMemSz ,
312
+ },
313
+ Debug : opts .Debug ,
208
314
}
209
315
210
316
if len (os .Args ) == 1 {
@@ -247,30 +353,23 @@ func main() {
247
353
machineOpts = append (machineOpts , firecracker .WithProcessRunner (cmd ))
248
354
}
249
355
250
- m , err := firecracker .NewMachine (fcCfg , machineOpts ... )
356
+ m , err := firecracker .NewMachine (vmmCtx , fcCfg , machineOpts ... )
251
357
if err != nil {
252
358
log .Fatalf ("Failed creating machine: %s" , err )
253
359
}
254
360
255
- errCh , err := m .Init (vmmCtx )
256
- if err != nil {
257
- log .Fatalf ("Firecracker Init returned error %s" , err )
258
- }
259
-
260
361
if metadata != nil {
261
- err := m .SetMetadata (vmmCtx , metadata )
262
- if err != nil {
263
- log .Fatalf ("Firecracker SetMetadata returned error %s" , err )
264
- }
362
+ m .EnableMetadata (metadata )
265
363
}
266
364
267
- if err := m .StartInstance (vmmCtx ); err != nil {
268
- log .Fatalf ("Failed to start instance : %v" , err )
365
+ if err := m .Start (vmmCtx ); err != nil {
366
+ log .Fatalf ("Failed to start machine : %v" , err )
269
367
}
368
+ defer m .StopVMM ()
270
369
271
370
// wait for the VMM to exit
272
- if err := <- errCh ; err != nil {
273
- log .Fatalf ("startVMM returned error %s" , err )
371
+ if err := m . Wait ( vmmCtx ) ; err != nil {
372
+ log .Fatalf ("Wait returned an error %s" , err )
274
373
}
275
- log .Printf ("startVMM was happy" )
374
+ log .Printf ("Start machine was happy" )
276
375
}
0 commit comments