Skip to content

Commit 433f262

Browse files
authored
Merge pull request #23 from xibz/fifo_logging_file
adding fifo logging files and refactoring the SDK
2 parents 6b08ec7 + 121ef9a commit 433f262

File tree

7 files changed

+1092
-254
lines changed

7 files changed

+1092
-254
lines changed

cmd/firectl/main.go

Lines changed: 135 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ import (
1818
"encoding/json"
1919
"errors"
2020
"fmt"
21+
"io"
22+
"io/ioutil"
2123
"os"
24+
"path/filepath"
2225
"strconv"
2326
"strings"
2427

2528
"github.com/firecracker-microvm/firecracker-go-sdk"
29+
models "github.com/firecracker-microvm/firecracker-go-sdk/client/models"
2630
"github.com/jessevdk/go-flags"
2731
log "github.com/sirupsen/logrus"
2832
)
@@ -43,28 +47,37 @@ const (
4347
executableMask = 0111
4448
)
4549

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+
5057
if strings.HasSuffix(entry, ":rw") {
58+
readOnly = false
5159
path = strings.TrimSuffix(entry, ":rw")
5260
} else if strings.HasSuffix(entry, ":ro") {
5361
path = strings.TrimSuffix(entry, ":ro")
5462
} else {
5563
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)
5765
}
66+
5867
if path == "" {
5968
return nil, errors.New("Invalid drive specification")
6069
}
61-
_, err := os.Stat(path)
62-
if err != nil {
70+
71+
if _, err := os.Stat(path); err != nil {
6372
return nil, err
6473
}
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),
6881
}
6982
devices = append(devices, e)
7083
}
@@ -105,6 +118,76 @@ func parseVsocks(devices []string) ([]firecracker.VsockDevice, error) {
105118
return result, nil
106119
}
107120

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+
108191
type options struct {
109192
FcBinary string `long:"firecracker-binary" description:"Path to firecracker binary"`
110193
FcKernelImage string `long:"kernel" description:"Path to the kernel image" default:"./vmlinux"`
@@ -122,6 +205,7 @@ type options struct {
122205
FcCPUTemplate string `long:"cpu-template" description:"Firecracker CPU Template (C3 or T2)"`
123206
FcMemSz int64 `long:"memory" short:"m" description:"VM memory, in MiB" default:"512"`
124207
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"`
125209
Debug bool `long:"debug" short:"d" description:"Enable debug output"`
126210
Help bool `long:"help" short:"h" description:"Show usage"`
127211
}
@@ -164,7 +248,7 @@ func main() {
164248
if err != nil {
165249
log.Fatalf("Unable to parse NIC config: %s", err)
166250
} else {
167-
log.Printf("Adding tap device %s", tapDev)
251+
log.Error("Adding tap device %s", tapDev)
168252
allowMDDS := metadata != nil
169253
NICs = []firecracker.NetworkInterface{
170254
firecracker.NetworkInterface{
@@ -176,35 +260,57 @@ func main() {
176260
}
177261
}
178262

179-
rootDrive := firecracker.BlockDevice{HostPath: opts.FcRootDrivePath, Mode: "rw"}
180-
181263
blockDevices, err := parseBlockDevices(opts.FcAdditionalDrives)
182264
if err != nil {
183265
log.Fatalf("Invalid block device specification: %s", err)
184266
}
185267

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+
186277
vsocks, err := parseVsocks(opts.FcVsockDevices)
187278
if err != nil {
188279
log.Fatalf("Invalid vsock specification: %s", err)
189280
}
190281

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+
191296
fcCfg := firecracker.Config{
192297
SocketPath: "./firecracker.sock",
193298
LogFifo: opts.FcLogFifo,
194299
LogLevel: opts.FcLogLevel,
195300
MetricsFifo: opts.FcMetricsFifo,
301+
FifoLogWriter: fifo,
196302
KernelImagePath: opts.FcKernelImage,
197303
KernelArgs: opts.FcKernelCmdLine,
198-
RootDrive: rootDrive,
199-
RootPartitionUUID: opts.FcRootPartUUID,
200-
AdditionalDrives: blockDevices,
304+
Drives: blockDevices,
201305
NetworkInterfaces: NICs,
202306
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,
208314
}
209315

210316
if len(os.Args) == 1 {
@@ -247,30 +353,23 @@ func main() {
247353
machineOpts = append(machineOpts, firecracker.WithProcessRunner(cmd))
248354
}
249355

250-
m, err := firecracker.NewMachine(fcCfg, machineOpts...)
356+
m, err := firecracker.NewMachine(vmmCtx, fcCfg, machineOpts...)
251357
if err != nil {
252358
log.Fatalf("Failed creating machine: %s", err)
253359
}
254360

255-
errCh, err := m.Init(vmmCtx)
256-
if err != nil {
257-
log.Fatalf("Firecracker Init returned error %s", err)
258-
}
259-
260361
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)
265363
}
266364

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)
269367
}
368+
defer m.StopVMM()
270369

271370
// 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)
274373
}
275-
log.Printf("startVMM was happy")
374+
log.Printf("Start machine was happy")
276375
}

example_test.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,25 @@ import (
66
"os"
77

88
"github.com/firecracker-microvm/firecracker-go-sdk"
9+
models "github.com/firecracker-microvm/firecracker-go-sdk/client/models"
910
)
1011

1112
func ExampleWithProcessRunner_logging() {
1213
const socketPath = "/tmp/firecracker.sock"
1314
cfg := firecracker.Config{
1415
SocketPath: socketPath,
1516
KernelImagePath: "/path/to/kernel",
16-
RootDrive: firecracker.BlockDevice{
17-
HostPath: "/path/to/root/drive",
18-
Mode: "rw",
17+
Drives: []models.Drive{
18+
models.Drive{
19+
DriveID: firecracker.String("1"),
20+
IsRootDevice: firecracker.Bool(true),
21+
IsReadOnly: firecracker.Bool(false),
22+
PathOnHost: firecracker.String("/path/to/root/drive"),
23+
},
24+
},
25+
MachineCfg: models.MachineConfiguration{
26+
VcpuCount: 1,
1927
},
20-
CPUCount: 1,
2128
}
2229

2330
// stdout will be directed to this file
@@ -44,22 +51,19 @@ func ExampleWithProcessRunner_logging() {
4451
WithStderr(stderr).
4552
Build(ctx)
4653

47-
m, err := firecracker.NewMachine(cfg, firecracker.WithProcessRunner(cmd))
54+
m, err := firecracker.NewMachine(ctx, cfg, firecracker.WithProcessRunner(cmd))
4855
if err != nil {
4956
panic(fmt.Errorf("failed to create new machine: %v", err))
5057
}
5158

5259
defer os.Remove(cfg.SocketPath)
5360

54-
ch, err := m.Init(ctx)
55-
if err != nil {
61+
if err := m.Start(ctx); err != nil {
5662
panic(fmt.Errorf("failed to initialize machine: %v", err))
5763
}
5864

59-
if err := m.StartInstance(ctx); err != nil {
60-
panic(fmt.Errorf("Failed to start instance: %v", err))
61-
}
62-
6365
// wait for VMM to execute
64-
<-ch
66+
if err := m.Wait(ctx); err != nil {
67+
panic(err)
68+
}
6569
}

0 commit comments

Comments
 (0)