@@ -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"`
@@ -117,12 +200,12 @@ type options struct {
117
200
FcLogFifo string `long:"vmm-log-fifo" description:"FIFO for firecracker logs"`
118
201
FcLogLevel string `long:"log-level" description:"vmm log level" default:"Debug"`
119
202
FcMetricsFifo string `long:"metrics-fifo" description:"FIFO for firecracker metrics"`
120
- FcCaptureFifoLogs bool `long:"capture-fifo-logs" description:"pipes fifo and metric fifo's contents to files"`
121
203
FcDisableHt bool `long:"disable-hyperthreading" short:"t" description:"Disable CPU Hyperthreading"`
122
204
FcCPUCount int64 `long:"ncpus" short:"c" description:"Number of CPUs" default:"1"`
123
205
FcCPUTemplate string `long:"cpu-template" description:"Firecracker CPU Template (C3 or T2)"`
124
206
FcMemSz int64 `long:"memory" short:"m" description:"VM memory, in MiB" default:"512"`
125
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"`
126
209
Debug bool `long:"debug" short:"d" description:"Enable debug output"`
127
210
Help bool `long:"help" short:"h" description:"Show usage"`
128
211
}
@@ -165,7 +248,7 @@ func main() {
165
248
if err != nil {
166
249
log .Fatalf ("Unable to parse NIC config: %s" , err )
167
250
} else {
168
- log .Printf ("Adding tap device %s" , tapDev )
251
+ log .Error ("Adding tap device %s" , tapDev )
169
252
allowMDDS := metadata != nil
170
253
NICs = []firecracker.NetworkInterface {
171
254
firecracker.NetworkInterface {
@@ -177,36 +260,57 @@ func main() {
177
260
}
178
261
}
179
262
180
- rootDrive := firecracker.BlockDevice {HostPath : opts .FcRootDrivePath , Mode : "rw" }
181
-
182
263
blockDevices , err := parseBlockDevices (opts .FcAdditionalDrives )
183
264
if err != nil {
184
265
log .Fatalf ("Invalid block device specification: %s" , err )
185
266
}
186
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
+
187
277
vsocks , err := parseVsocks (opts .FcVsockDevices )
188
278
if err != nil {
189
279
log .Fatalf ("Invalid vsock specification: %s" , err )
190
280
}
191
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
+
192
296
fcCfg := firecracker.Config {
193
- SocketPath : "./firecracker.sock" ,
194
- LogFifo : opts .FcLogFifo ,
195
- LogLevel : opts .FcLogLevel ,
196
- MetricsFifo : opts .FcMetricsFifo ,
197
- CaptureFifoLogsToFile : opts . FcCaptureFifoLogs ,
198
- KernelImagePath : opts .FcKernelImage ,
199
- KernelArgs : opts .FcKernelCmdLine ,
200
- RootDrive : rootDrive ,
201
- RootPartitionUUID : opts . FcRootPartUUID ,
202
- AdditionalDrives : blockDevices ,
203
- NetworkInterfaces : NICs ,
204
- VsockDevices : vsocks ,
205
- CPUCount : opts .FcCPUCount ,
206
- CPUTemplate : firecracker . CPUTemplate ( opts .FcCPUTemplate ) ,
207
- HtEnabled : ! opts .FcDisableHt ,
208
- MemInMiB : opts . FcMemSz ,
209
- Debug : opts .Debug ,
297
+ SocketPath : "./firecracker.sock" ,
298
+ LogFifo : opts .FcLogFifo ,
299
+ LogLevel : opts .FcLogLevel ,
300
+ MetricsFifo : opts .FcMetricsFifo ,
301
+ FifoLogWriter : fifo ,
302
+ KernelImagePath : opts .FcKernelImage ,
303
+ KernelArgs : opts .FcKernelCmdLine ,
304
+ Drives : blockDevices ,
305
+ NetworkInterfaces : NICs ,
306
+ VsockDevices : vsocks ,
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 ,
210
314
}
211
315
212
316
if len (os .Args ) == 1 {
@@ -249,29 +353,23 @@ func main() {
249
353
machineOpts = append (machineOpts , firecracker .WithProcessRunner (cmd ))
250
354
}
251
355
252
- m , err := firecracker .NewMachine (fcCfg , machineOpts ... )
356
+ m , err := firecracker .NewMachine (vmmCtx , fcCfg , machineOpts ... )
253
357
if err != nil {
254
358
log .Fatalf ("Failed creating machine: %s" , err )
255
359
}
256
360
257
- if err := m .Init (vmmCtx ); err != nil {
258
- log .Fatalf ("Firecracker Init returned error %s" , err )
259
- }
260
-
261
361
if metadata != nil {
262
- err := m .SetMetadata (vmmCtx , metadata )
263
- if err != nil {
264
- log .Fatalf ("Firecracker SetMetadata returned error %s" , err )
265
- }
362
+ m .EnableMetadata (metadata )
266
363
}
267
364
268
- if err := m .StartInstance (vmmCtx ); err != nil {
269
- 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 )
270
367
}
368
+ defer m .StopVMM ()
271
369
272
370
// wait for the VMM to exit
273
- if err := <- m . ErrCh ; err != nil {
274
- log .Fatalf ("startVMM returned error %s" , err )
371
+ if err := m . Wait ( vmmCtx ) ; err != nil {
372
+ log .Fatalf ("Wait returned an error %s" , err )
275
373
}
276
- log .Printf ("startVMM was happy" )
374
+ log .Printf ("Start machine was happy" )
277
375
}
0 commit comments