@@ -13,9 +13,9 @@ import (
1313 "os"
1414 "path/filepath"
1515 "strings"
16+ "text/template"
1617 "time"
1718
18- // "github.com/codegangsta/cli"
1919 "github.com/docker/machine/libmachine/drivers"
2020 "github.com/docker/machine/libmachine/log"
2121 "github.com/docker/machine/libmachine/mcnflag"
@@ -32,10 +32,9 @@ const (
3232 dnsmasqLeases = "/var/lib/libvirt/dnsmasq/%s.leases"
3333 dnsmasqStatus = "/var/lib/libvirt/dnsmasq/%s.status"
3434
35- // TODO - Switch to template based instead of sprintf substitution
36- domainXML = `<domain type='kvm'>
37- <name>%s</name> <memory unit='M'>%d</memory>
38- <vcpu>%d</vcpu>
35+ domainXMLTemplate = `<domain type='kvm'>
36+ <name>{{.MachineName}}</name> <memory unit='M'>{{.Memory}}</memory>
37+ <vcpu>{{.CPU}}</vcpu>
3938 <features><acpi/><apic/><pae/></features>
4039 <os>
4140 <type>hvm</type>
@@ -45,22 +44,22 @@ const (
4544 </os>
4645 <devices>
4746 <disk type='file' device='cdrom'>
48- <source file='%s '/>
47+ <source file='{{.ISO}} '/>
4948 <target dev='hdc' bus='ide'/>
5049 <readonly/>
5150 </disk>
5251 <disk type='file' device='disk'>
53- <source file='%s '/>
52+ <source file='{{.DiskPath}} '/>
5453 <target dev='hda' bus='ide'/>
5554 </disk>
5655 <graphics type='vnc' autoport='yes' listen='127.0.0.1'>
5756 <listen type='address' address='127.0.0.1'/>
5857 </graphics>
5958 <interface type='network'>
60- <source network='%s '/>
59+ <source network='{{.Network}} '/>
6160 </interface>
6261 <interface type='network'>
63- <source network='%s '/>
62+ <source network='{{.PrivateNetwork}} '/>
6463 </interface>
6564 </devices>
6665</domain>`
@@ -81,10 +80,12 @@ type Driver struct {
8180 DiskSize int
8281 CPU int
8382 Network string
83+ PrivateNetwork string
8484 ISO string
8585 Boot2DockerURL string
8686 CaCertPath string
8787 PrivateKeyPath string
88+ DiskPath string
8889 connectionString string
8990 conn * libvirt.VirConnection
9091 VM * libvirt.VirDomain
@@ -93,19 +94,6 @@ type Driver struct {
9394
9495func (d * Driver ) GetCreateFlags () []mcnflag.Flag {
9596 return []mcnflag.Flag {
96- /*
97- * Can't support this at present due to filesystem assumptions
98- * If we can figure out how to copy the disk image up
99- * to the remote system, then we could support remote libvirt
100- * instances
101- */
102- /*
103- mcnflag.Flag{
104- Name: "kvm-connection",
105- Usage: "The libvirt connection string",
106- Value: "qemu:///system",
107- },
108- */
10997 mcnflag.Flag {
11098 Name : "kvm-memory" ,
11199 Usage : "Size of memory for host in MB" ,
@@ -133,6 +121,12 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
133121 Usage : "The URL of the boot2docker image. Defaults to the latest available version" ,
134122 Value : "" ,
135123 },
124+ /* Not yet implemented
125+ mcnflag.Flag{
126+ Name: "kvm-no-share",
127+ Usage: "Disable the mount of your home directory",
128+ },
129+ */
136130 }
137131}
138132
@@ -179,10 +173,10 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
179173 d .SwarmMaster = flags .Bool ("swarm-master" )
180174 d .SwarmHost = flags .String ("swarm-host" )
181175 d .SwarmDiscovery = flags .String ("swarm-discovery" )
176+ d .ISO = filepath .Join (d .LocalArtifactPath ("." ), isoFilename )
182177 d .SSHUser = "docker"
183178 d .SSHPort = 22
184- d .ISO = filepath .Join (d .LocalArtifactPath ("." ), isoFilename )
185-
179+ d .DiskPath = filepath .Join (d .LocalArtifactPath ("." ), fmt .Sprintf ("%s.img" , d .MachineName ))
186180 return nil
187181}
188182
@@ -196,25 +190,62 @@ func (d *Driver) GetURL() (string, error) {
196190 if ip == "" {
197191 return "" , nil
198192 }
199- return fmt .Sprintf ("tcp://%s:2376" , ip ), nil
193+ return fmt .Sprintf ("tcp://%s:2376" , ip ), nil // TODO - don't hardcode the port!
200194}
201195
202196// Create, or verify the private network is properly configured
203197func (d * Driver ) validatePrivateNetwork () error {
204198 log .Debug ("Validating private network" )
205- _ , err := d .conn .LookupNetworkByName (privateNetworkName )
199+ network , err := d .conn .LookupNetworkByName (d . PrivateNetwork )
206200 if err == nil {
207- // TODO - validate the proper configuration
201+ xmldoc , err := network .GetXMLDesc (0 )
202+ if err != nil {
203+ return err
204+ }
205+ /* XML structure:
206+ <network>
207+ ...
208+ <ip address='a.b.c.d' netmask='255.255.255.0'>
209+ <dhcp>
210+ <range start='a.b.c.d' end='w.x.y.z'/>
211+ </dhcp>
212+ */
213+ type Ip struct {
214+ Address string `xml:"address,attr"`
215+ Netmask string `xml:"netmask,attr"`
216+ }
217+ type Network struct {
218+ Ip Ip `xml:"ip"`
219+ }
220+
221+ var nw Network
222+ err = xml .Unmarshal ([]byte (xmldoc ), & nw )
223+ if err != nil {
224+ return err
225+ }
226+
227+ if nw .Ip .Address == "" {
228+ return fmt .Errorf ("%s network doesn't have DHCP configured properly" , d .PrivateNetwork )
229+ }
230+ // Corner case, but might happen...
231+ if active , err := network .IsActive (); ! active {
232+ log .Debugf ("Reactivating private network: %s" , err )
233+ err = network .Create ()
234+ if err != nil {
235+ log .Warnf ("Failed to Start network: %s" , err )
236+ return err
237+ }
238+ }
208239 return nil
209240 }
210241 // TODO - try a couple pre-defined networks and look for conflicts before
211242 // settling on one
212- xml := fmt .Sprintf (networkXML , privateNetworkName ,
243+ xml := fmt .Sprintf (networkXML , d . PrivateNetwork ,
213244 "192.168.42.1" ,
214245 "255.255.255.0" ,
215246 "192.168.42.2" ,
216247 "192.168.42.254" )
217- network , err : = d .conn .NetworkDefineXML (xml )
248+ network , err = d .conn .NetworkDefineXML (xml )
218249 if err != nil {
219250 log .Errorf ("Failed to create private network: %s" , err )
220251 return nil
@@ -242,8 +273,8 @@ func (d *Driver) validateNetwork(name string) error {
242273}
243274
244275func (d * Driver ) PreCreateCheck () error {
245- // We could look at d.conn.GetCapabilities()
246- // parse the XML, and look for hypervisors we care about
276+ // TODO We could look at d.conn.GetCapabilities()
277+ // parse the XML, and look for kvm
247278
248279 log .Debug ("About to check libvirt version" )
249280
@@ -302,10 +333,17 @@ func (d *Driver) Create() error {
302333 }
303334
304335 log .Debugf ("Defining VM..." )
305- // TODO Needs love for other tunables users might want to tweak
306- xml := fmt .Sprintf (domainXML , d .MachineName , d .Memory , d .CPU ,
307- d .ISO , d .diskPath (), d .Network , privateNetworkName )
308- vm , err := d .conn .DomainDefineXML (xml )
336+ tmpl , err := template .New ("domain" ).Parse (domainXMLTemplate )
337+ if err != nil {
338+ return err
339+ }
340+ var xml bytes.Buffer
341+ err = tmpl .Execute (& xml , d )
342+ if err != nil {
343+ return err
344+ }
345+
346+ vm , err := d .conn .DomainDefineXML (xml .String ())
309347 if err != nil {
310348 log .Warnf ("Failed to create the VM: %s" , err )
311349 return err
@@ -443,17 +481,18 @@ func (d *Driver) getMAC() (string, error) {
443481 if err != nil {
444482 return "" , err
445483 }
446- // XML structure:
447- // <domain>
448- // ...
449- // <devices>
450- // ...
451- // <interface type='network'>
452- // ...
453- // <mac address='52:54:00:d2:3f:ba'/>
454- // ...
455- // </interface>
456- // ...
484+ /* XML structure:
485+ <domain>
486+ ...
487+ <devices>
488+ ...
489+ <interface type='network'>
490+ ...
491+ <mac address='52:54:00:d2:3f:ba'/>
492+ ...
493+ </interface>
494+ ...
495+ */
457496 type Mac struct {
458497 Address string `xml:"address,attr"`
459498 }
@@ -483,7 +522,7 @@ func (d *Driver) getMAC() (string, error) {
483522}
484523
485524func (d * Driver ) getIPByMACFromLeaseFile (mac string ) (string , error ) {
486- leaseFile := fmt .Sprintf (dnsmasqLeases , privateNetworkName )
525+ leaseFile := fmt .Sprintf (dnsmasqLeases , d . PrivateNetwork )
487526 data , err := ioutil .ReadFile (leaseFile )
488527 if err != nil {
489528 log .Debugf ("Failed to retrieve dnsmasq leases from %s" , leaseFile )
@@ -507,7 +546,7 @@ func (d *Driver) getIPByMACFromLeaseFile(mac string) (string, error) {
507546}
508547
509548func (d * Driver ) getIPByMacFromSettings (mac string ) (string , error ) {
510- network , err := d .conn .LookupNetworkByName (privateNetworkName )
549+ network , err := d .conn .LookupNetworkByName (d . PrivateNetwork )
511550 if err != nil {
512551 log .Warnf ("Failed to find network: %s" , err )
513552 return "" , err
@@ -522,7 +561,7 @@ func (d *Driver) getIPByMacFromSettings(mac string) (string, error) {
522561 type Lease struct {
523562 Ip_address string `json:"ip-address"`
524563 Mac_address string `json:"mac-address"`
525- /* Other unused fields omitted */
564+ // Other unused fields omitted
526565 }
527566 var s []Lease
528567
@@ -561,10 +600,6 @@ func (d *Driver) publicSSHKeyPath() string {
561600 return d .GetSSHKeyPath () + ".pub"
562601}
563602
564- func (d * Driver ) diskPath () string {
565- return filepath .Join (d .LocalArtifactPath ("." ), fmt .Sprintf ("%s.img" , d .MachineName ))
566- }
567-
568603// Make a boot2docker VM disk image.
569604func (d * Driver ) generateDiskImage (size int ) error {
570605 log .Debugf ("Creating %d MB hard disk image..." , size )
@@ -609,7 +644,7 @@ func (d *Driver) generateDiskImage(size int) error {
609644 return err
610645 }
611646 raw := bytes .NewReader (buf .Bytes ())
612- return createDiskImage (d .diskPath () , size , raw )
647+ return createDiskImage (d .DiskPath , size , raw )
613648}
614649
615650// createDiskImage makes a disk image at dest with the given size in MB. If r is
@@ -639,5 +674,6 @@ func NewDriver() *Driver {
639674 log .Fatalf ("Failed to connect to libvirt: %s" , err )
640675 }
641676 d .conn = & conn
677+ d .PrivateNetwork = privateNetworkName
642678 return d
643679}
0 commit comments