Skip to content
This repository was archived by the owner on Feb 27, 2018. It is now read-only.

Commit 63ff5c8

Browse files
committed
Merge pull request #265 from vmware/fusion-bindings
Add Fusion driver and vmx template
2 parents 43241cb + 7fb5cbd commit 63ff5c8

8 files changed

Lines changed: 372 additions & 3 deletions

File tree

cmds.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"github.com/boot2docker/boot2docker-cli/driver"
1717
_ "github.com/boot2docker/boot2docker-cli/dummy"
18+
_ "github.com/boot2docker/boot2docker-cli/fusion"
1819
_ "github.com/boot2docker/boot2docker-cli/virtualbox"
1920
)
2021

driver/driver.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type Machine interface {
4242
AttachStorage(ctlName string, medium StorageMedium) error
4343
GetState() MachineState
4444
GetName() string
45+
GetHostname() string
4546
GetSerialFile() string
4647
GetDockerPort() uint
4748
GetSSHPort() uint

dummy/machine.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ func ConfigFlags(B2D *driver.MachineConfig, flags *flag.FlagSet) error {
4747

4848
// Machine information.
4949
type Machine struct {
50-
Name string
5150
UUID string
51+
Name string
52+
Hostname string
5253
State driver.MachineState
5354
CPUs uint
5455
Memory uint // main memory (in MB)
@@ -122,6 +123,11 @@ func (m *Machine) GetName() string {
122123
return m.Name
123124
}
124125

126+
// Get machine hostname
127+
func (m *Machine) GetHostname() string {
128+
return m.Hostname
129+
}
130+
125131
// Get current state
126132
func (m *Machine) GetState() driver.MachineState {
127133
return m.State

fusion/machine.go

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
package fusion
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strconv"
9+
"strings"
10+
"text/template"
11+
12+
"github.com/boot2docker/boot2docker-cli/driver"
13+
"github.com/ogier/pflag"
14+
)
15+
16+
var (
17+
verbose bool // Verbose mode.
18+
cfg DriverCfg
19+
)
20+
21+
type DriverCfg struct {
22+
VMRUN string // Path to vmrun utility.
23+
VDISKMAN string // Path to vdiskmanager utility.
24+
}
25+
26+
func init() {
27+
if err := driver.Register("fusion", InitFunc); err != nil {
28+
fmt.Fprintf(os.Stderr, "Failed to initialize driver. Error : %s", err.Error())
29+
os.Exit(1)
30+
}
31+
if err := driver.RegisterConfig("fusion", ConfigFlags); err != nil {
32+
fmt.Fprintf(os.Stderr, "Failed to initialize driver config. Error : %s", err.Error())
33+
os.Exit(1)
34+
}
35+
}
36+
37+
// Initialize the Machine.
38+
func InitFunc(mc *driver.MachineConfig) (driver.Machine, error) {
39+
verbose = mc.Verbose
40+
41+
m, err := GetMachine(getVMX(mc))
42+
if err != nil && mc.Init == true {
43+
return CreateMachine(mc)
44+
}
45+
return m, err
46+
}
47+
48+
// Add cmdline params for this driver
49+
func ConfigFlags(mc *driver.MachineConfig, flags *pflag.FlagSet) error {
50+
cfg.VMRUN = "/Applications/VMware Fusion.app/Contents/Library/vmrun"
51+
flags.StringVar(&cfg.VMRUN, "vmrun", cfg.VMRUN, "path to vmrun utility.")
52+
53+
cfg.VDISKMAN = "/Applications/VMware Fusion.app/Contents/Library/vmware-vdiskmanager"
54+
flags.StringVar(&cfg.VDISKMAN, "vmdiskman", cfg.VDISKMAN, "path to vdiskmanager utility.")
55+
56+
return nil
57+
}
58+
59+
// Machine information.
60+
type Machine struct {
61+
Name string
62+
State driver.MachineState
63+
CPUs uint64
64+
Memory uint64 // main memory (in MB)
65+
VMX string
66+
OSType string
67+
}
68+
69+
// Refresh reloads the machine information.
70+
func (m *Machine) Refresh() error {
71+
mm, err := GetMachine(m.VMX)
72+
if err != nil {
73+
return err
74+
}
75+
*m = *mm
76+
return nil
77+
}
78+
79+
// Start starts the machine.
80+
func (m *Machine) Start() error {
81+
vmrun("start", m.VMX, "nogui")
82+
return nil
83+
}
84+
85+
// Suspend suspends the machine and saves its state to disk.
86+
func (m *Machine) Save() error {
87+
vmrun("suspend", m.VMX, "nogui")
88+
return nil
89+
}
90+
91+
// Pause pauses the execution of the machine.
92+
func (m *Machine) Pause() error {
93+
vmrun("pause", m.VMX, "nogui")
94+
return nil
95+
}
96+
97+
// Stop gracefully stops the machine.
98+
func (m *Machine) Stop() error {
99+
vmrun("stop", m.VMX, "nogui")
100+
return nil
101+
}
102+
103+
// Poweroff forcefully stops the machine. State is lost and might corrupt the disk image.
104+
func (m *Machine) Poweroff() error {
105+
vmrun("stop", m.VMX, "nogui")
106+
return nil
107+
}
108+
109+
// Restart gracefully restarts the machine.
110+
func (m *Machine) Restart() error {
111+
vmrun("reset", m.VMX, "nogui")
112+
return nil
113+
}
114+
115+
// Reset forcefully restarts the machine. State is lost and might corrupt the disk image.
116+
func (m *Machine) Reset() error {
117+
vmrun("reset", m.VMX, "nogui")
118+
return nil
119+
}
120+
121+
// Get vm name
122+
func (m *Machine) GetName() string {
123+
return m.Name
124+
}
125+
126+
// Get vm hostname
127+
func (m *Machine) GetHostname() string {
128+
stdout, _, _ := vmrun("getGuestIPAddress", m.VMX)
129+
return strings.TrimSpace(stdout)
130+
}
131+
132+
// Get current state
133+
func (m *Machine) GetState() driver.MachineState {
134+
return m.State
135+
}
136+
137+
// Get serial file
138+
func (m *Machine) GetSerialFile() string {
139+
return ""
140+
}
141+
142+
// Get Docker port
143+
func (m *Machine) GetDockerPort() uint {
144+
return 2375
145+
}
146+
147+
// Get SSH port
148+
func (m *Machine) GetSSHPort() uint {
149+
return 22
150+
}
151+
152+
// Delete deletes the machine and associated disk images.
153+
func (m *Machine) Delete() error {
154+
vmrun("deleteVM", m.VMX, "nogui")
155+
return nil
156+
}
157+
158+
// Modify changes the settings of the machine.
159+
func (m *Machine) Modify() error {
160+
fmt.Printf("Hot modify not supported")
161+
return m.Refresh()
162+
}
163+
164+
// AddNATPF adds a NAT port forarding rule to the n-th NIC with the given name.
165+
func (m *Machine) AddNATPF(n int, name string, rule driver.PFRule) error {
166+
fmt.Println("Add NAT PF")
167+
return nil
168+
}
169+
170+
// DelNATPF deletes the NAT port forwarding rule with the given name from the n-th NIC.
171+
func (m *Machine) DelNATPF(n int, name string) error {
172+
fmt.Println("Del NAT PF")
173+
return nil
174+
}
175+
176+
// SetNIC set the n-th NIC.
177+
func (m *Machine) SetNIC(n int, nic driver.NIC) error {
178+
fmt.Println("Set NIC")
179+
return nil
180+
}
181+
182+
// AddStorageCtl adds a storage controller with the given name.
183+
func (m *Machine) AddStorageCtl(name string, ctl driver.StorageController) error {
184+
fmt.Println("Add storage ctl")
185+
return nil
186+
}
187+
188+
// DelStorageCtl deletes the storage controller with the given name.
189+
func (m *Machine) DelStorageCtl(name string) error {
190+
fmt.Println("Del storage ctl")
191+
return nil
192+
}
193+
194+
// AttachStorage attaches a storage medium to the named storage controller.
195+
func (m *Machine) AttachStorage(ctlName string, medium driver.StorageMedium) error {
196+
fmt.Println("Attach storage")
197+
return nil
198+
}
199+
200+
// GetMachine finds a machine.
201+
func GetMachine(vmx string) (*Machine, error) {
202+
if _, err := os.Stat(vmx); os.IsNotExist(err) {
203+
return nil, ErrMachineNotExist
204+
}
205+
206+
m := &Machine{VMX: vmx, State: driver.Poweroff}
207+
208+
// VMRUN only tells use if the vm is running or not
209+
if stdout, _, _ := vmrun("list"); strings.Contains(stdout, m.VMX) {
210+
m.State = driver.Running
211+
}
212+
213+
// Parse the vmx file
214+
vmxfile, err := os.Open(vmx)
215+
if err != nil {
216+
return m, err
217+
}
218+
defer vmxfile.Close()
219+
220+
vmxscan := bufio.NewScanner(vmxfile)
221+
for vmxscan.Scan() {
222+
if vmxtokens := strings.Split(vmxscan.Text(), " = "); len(vmxtokens) > 1 {
223+
vmxkey := strings.TrimSpace(vmxtokens[0])
224+
vmxvalue, _ := strconv.Unquote(vmxtokens[1])
225+
switch vmxkey {
226+
case "displayName":
227+
m.Name = vmxvalue
228+
case "guestOS":
229+
m.OSType = vmxvalue
230+
case "memsize":
231+
m.Memory, _ = strconv.ParseUint(vmxvalue, 10, 0)
232+
case "numvcpus":
233+
m.CPUs, _ = strconv.ParseUint(vmxvalue, 10, 0)
234+
}
235+
}
236+
}
237+
return m, nil
238+
}
239+
240+
// CreateMachine creates a new virtual machine.
241+
func CreateMachine(mc *driver.MachineConfig) (*Machine, error) {
242+
if err := os.MkdirAll(getBaseFolder(mc), 0755); err != nil {
243+
return nil, err
244+
}
245+
246+
if _, err := os.Stat(getVMX(mc)); err == nil {
247+
return nil, ErrMachineExist
248+
}
249+
250+
// Generate vmx config file from template
251+
vmxt := template.Must(template.New("vmx").Parse(vmx))
252+
vmxfile, err := os.Create(getVMX(mc))
253+
if err != nil {
254+
return nil, err
255+
}
256+
vmxt.Execute(vmxfile, mc)
257+
258+
// Generate vmdk file
259+
diskImg := filepath.Join(getBaseFolder(mc), fmt.Sprintf("%s.vmdk", mc.VM))
260+
if _, err := os.Stat(diskImg); err != nil {
261+
if !os.IsNotExist(err) {
262+
return nil, err
263+
}
264+
265+
if err := vdiskmanager(diskImg, mc.DiskSize); err != nil {
266+
return nil, err
267+
}
268+
}
269+
270+
return nil, nil
271+
}
272+
273+
func getBaseFolder(mc *driver.MachineConfig) string {
274+
return filepath.Join(mc.Dir, mc.VM)
275+
}
276+
func getVMX(mc *driver.MachineConfig) string {
277+
return filepath.Join(getBaseFolder(mc), fmt.Sprintf("%s.vmx", mc.VM))
278+
}

fusion/vmrun.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package fusion
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"fmt"
7+
"log"
8+
"os/exec"
9+
"strings"
10+
)
11+
12+
var (
13+
ErrMachineExist = errors.New("machine already exists")
14+
ErrMachineNotExist = errors.New("machine does not exist")
15+
ErrVMRUNNotFound = errors.New("VMRUN not found")
16+
)
17+
18+
func vmrun(args ...string) (string, string, error) {
19+
cmd := exec.Command(cfg.VMRUN, args...)
20+
if verbose {
21+
log.Printf("executing: %v %v", cfg.VMRUN, strings.Join(args, " "))
22+
}
23+
24+
var stdout bytes.Buffer
25+
var stderr bytes.Buffer
26+
cmd.Stdout, cmd.Stderr = &stdout, &stderr
27+
28+
err := cmd.Run()
29+
if err != nil {
30+
if ee, ok := err.(*exec.Error); ok && ee == exec.ErrNotFound {
31+
err = ErrVMRUNNotFound
32+
}
33+
}
34+
35+
return stdout.String(), stderr.String(), err
36+
}
37+
38+
// Make a vmdk disk image with the given size (in MB).
39+
func vdiskmanager(dest string, size uint) error {
40+
cmd := exec.Command(cfg.VDISKMAN, "-c", "-t", "0", "-s", fmt.Sprintf("%dMB", size), "-a", "lsilogic", dest)
41+
42+
if stdout := cmd.Run(); stdout != nil {
43+
if ee, ok := stdout.(*exec.Error); ok && ee == exec.ErrNotFound {
44+
return ErrVMRUNNotFound
45+
}
46+
}
47+
return nil
48+
}

fusion/vmx.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package fusion
2+
3+
const vmx = `
4+
.encoding = "UTF-8"
5+
config.version = "8"
6+
displayName = "{{.VM}}"
7+
ethernet0.addressType = "generated"
8+
ethernet0.connectionType = "nat"
9+
ethernet0.linkStatePropagation.enable = "TRUE"
10+
ethernet0.present = "TRUE"
11+
ethernet0.virtualDev = "e1000"
12+
ethernet0.wakeOnPcktRcv = "FALSE"
13+
floppy0.present = "FALSE"
14+
guestOS = "other26xlinux-64"
15+
hpet0.present = "TRUE"
16+
ide1:0.deviceType = "cdrom-image"
17+
ide1:0.fileName = "{{.ISO}}"
18+
ide1:0.present = "TRUE"
19+
mem.hotadd = "TRUE"
20+
memsize = "{{.Memory}}"
21+
powerType.powerOff = "soft"
22+
powerType.powerOn = "soft"
23+
powerType.reset = "soft"
24+
powerType.suspend = "soft"
25+
scsi0.present = "TRUE"
26+
scsi0.virtualDev = "lsilogic"
27+
scsi0:0.fileName = "{{.VM}}.vmdk"
28+
scsi0:0.present = "TRUE"
29+
virtualHW.productCompatibility = "hosted"
30+
virtualHW.version = "10"
31+
`

0 commit comments

Comments
 (0)