diff options
author | Flavio Castelli <fcastelli@suse.com> | 2017-06-19 21:09:04 +0200 |
---|---|---|
committer | Flavio Castelli <fcastelli@suse.com> | 2017-06-19 21:09:04 +0200 |
commit | 4307ba55c5d4cabf750756f243bed97505f8e61a (patch) | |
tree | 2e449e111ea5ae5411ccb95534dc921917d242dd | |
parent | 1b6657408ec4259426d7420fe48ae97415428f3c (diff) | |
download | terraform-provider-libvirt-4307ba55c5d4cabf750756f243bed97505f8e61a.tar terraform-provider-libvirt-4307ba55c5d4cabf750756f243bed97505f8e61a.tar.gz |
Add support of filesystem device to domain
Allow sharing of directories of the host with the guest by using the
filesystem device.
-rw-r--r-- | docs/providers/libvirt/r/domain.html.markdown | 44 | ||||
-rw-r--r-- | libvirt/domain_def.go | 54 | ||||
-rw-r--r-- | libvirt/resource_libvirt_domain.go | 54 | ||||
-rw-r--r-- | libvirt/resource_libvirt_domain_test.go | 45 |
4 files changed, 195 insertions, 2 deletions
diff --git a/docs/providers/libvirt/r/domain.html.markdown b/docs/providers/libvirt/r/domain.html.markdown index 3305973b..a78b68d3 100644 --- a/docs/providers/libvirt/r/domain.html.markdown +++ b/docs/providers/libvirt/r/domain.html.markdown @@ -41,6 +41,7 @@ The following arguments are supported: cloud-init won't cause the domain to be recreated, however the change will have effect on the next reboot. * `cpu` - (Optional) Configures CPU mode. +* `filesystem` - (Optional) An array of one or more host filesystems to attach to the domain. The `filesystem` object structure is documented below. There is an optional `coreos_ignition` parameter: * `coreos_ignition` (Optional) The `libvirt_ignition` resource that is to be used by @@ -296,6 +297,49 @@ resource "libvirt_domain" "my_machine" { } ``` + +The optional `filesystem` block allows to define one or more [filesytem](https://libvirt.org/formatdomain.html#elementsFilesystems) +entries to be added to the domain. This allows to share a directory of the libvirtd +host with the guest. + +Currently the following attributes are supported: + + * `accessmode`: specifies the security mode for accessing the source. By default + the `mapped` mode is chosen. + * `source`: the directory of the host to be shared with the guest. + * `target`: an arbitrary string tag that is exported to the guest as a hint for + where to mount the source. + * `readonly`: enables exporting filesystem as a readonly mount for guest, by + default read-only access is given. + +Example: + +``` +filesystem { + source = "/tmp" + target = "tmp" + readonly = false +} +filesystem { + source = "/proc" + target = "proc" + readonly = true +} +``` + +The exported filesystems can be mounted inside of the guest in this way: + +``` +sudo mount -t 9p -o trans=virtio,version=9p2000.L,rw tmp /host/tmp +sudo mount -t 9p -o trans=virtio,version=9p2000.L,r proc /host/proc +``` + +This can be automated inside of `/etc/fstab`: +``` +tmp /host/tmp 9p trans=virtio,version=9p2000.L,rw 0 0 +proc /host/proc 9p trans=virtio,version=9p2000.L,r 0 0 +``` + ## Attributes Reference * `id` - a unique identifier for the resource diff --git a/libvirt/domain_def.go b/libvirt/domain_def.go index a4661bc2..c656d095 100644 --- a/libvirt/domain_def.go +++ b/libvirt/domain_def.go @@ -27,6 +27,7 @@ type defDomain struct { Disks []defDisk `xml:"disk"` NetworkInterfaces []defNetworkInterface `xml:"interface"` Console []defConsole `xml:"console"` + Filesystems []defFilesystem `xml:"filesystem"` Graphics *defGraphics `xml:"graphics,omitempty"` // QEMU guest agent channel QemuGAChannel struct { @@ -121,6 +122,59 @@ type defConsole struct { } `xml:"target"` } +type defFilesystemReadOnly bool + +type defFilesystem struct { + Type string `xml:"type,attr"` + AccessMode string `xml:"accessmode,attr"` + ReadOnly defFilesystemReadOnly `xml:"readonly",omitempty` + Source struct { + Dir string `xml:"dir,attr,omitempty"` + } `xml:"source"` + Target struct { + Dir string `xml:"dir,attr,omitempty"` + } `xml:"target"` +} + +// The filesystem element has a <readonly/> tag when +// the host directory cannot be written by the guest. When +// the <readonly/> tag is omitted the read-write permissions +// are granted. +// To show the empty <readonly/> tag we have to define a +// "alias" of bool and provide a custom marshaller for it. +func (r defFilesystemReadOnly) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + if !r { + return nil + } + err := e.EncodeElement("", start) + return err +} + +func (r *defFilesystemReadOnly) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + var value string + err := d.DecodeElement(&value, &start) + if err != nil { + return err + } + + *r = value == "" + + return nil +} + +func newFilesystemDef() defFilesystem { + fs := defFilesystem{} + + // This is the only type used by qemu/kvm + fs.Type = "mount" + + // A safe default value + fs.AccessMode = "mapped" + fs.ReadOnly = true + + return fs +} + // Creates a domain definition with the defaults // the provider uses func newDomainDef() defDomain { diff --git a/libvirt/resource_libvirt_domain.go b/libvirt/resource_libvirt_domain.go index f53944fb..e94edb1b 100644 --- a/libvirt/resource_libvirt_domain.go +++ b/libvirt/resource_libvirt_domain.go @@ -95,6 +95,15 @@ func resourceLibvirtDomain() *schema.Resource { ForceNew: true, Default: "", }, + "filesystem": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Required: false, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeMap, + }, + }, "disk": &schema.Schema{ Type: schema.TypeList, Optional: true, @@ -289,6 +298,34 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error domainDef.Devices.Controller = append(domainDef.Devices.Controller, controller) } + filesystemsCount := d.Get("filesystem.#").(int) + var filesystems []defFilesystem + for i := 0; i < filesystemsCount; i++ { + fs := newFilesystemDef() + + fsKey := fmt.Sprintf("filesystem.%d", i) + fsMap := d.Get(fsKey).(map[string]interface{}) + if accessMode, ok := fsMap["accessmode"]; ok { + fs.AccessMode = accessMode.(string) + } + if sourceDir, ok := fsMap["source"]; ok { + fs.Source.Dir = sourceDir.(string) + } else { + return fmt.Errorf("Filesystem entry must have a 'source' set") + } + if targetDir, ok := fsMap["target"]; ok { + fs.Target.Dir = targetDir.(string) + } else { + return fmt.Errorf("Filesystem entry must have a 'target' set") + } + if readonly, ok := fsMap["readonly"]; ok { + fs.ReadOnly = readonly.(string) == "1" + } + + filesystems = append(filesystems, fs) + } + log.Printf("filesystems: %+v\n", filesystems) + type pendingMapping struct { mac string hostname string @@ -421,6 +458,7 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error } domainDef.Devices.Disks = disks + domainDef.Devices.Filesystems = filesystems domainDef.Devices.NetworkInterfaces = netIfaces connectURI, err := virConn.GetURI() @@ -434,9 +472,9 @@ func resourceLibvirtDomainCreate(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("Error serializing libvirt domain: %s", err) } - log.Printf("[DEBUG] Creating libvirt domain with XML:\n%s", string(data)) + log.Printf("[DEBUG] Creating libvirt domain with XML:\n%s", data) - domain, err := virConn.DomainDefineXML(string(data)) + domain, err := virConn.DomainDefineXML(data) if err != nil { return fmt.Errorf("Error defining libvirt domain: %s", err) } @@ -699,6 +737,18 @@ func resourceLibvirtDomainRead(d *schema.ResourceData, meta interface{}) error { } d.Set("disks", disks) + filesystems := make([]map[string]interface{}, 0) + for _, fsDef := range domainDef.Devices.Filesystems { + fs := map[string]interface{}{ + "accessmode": fsDef.AccessMode, + "source": fsDef.Source.Dir, + "target": fsDef.Target.Dir, + "readonly": fsDef.ReadOnly, + } + filesystems = append(filesystems, fs) + } + d.Set("filesystems", filesystems) + // look interfaces with addresses ifacesWithAddr, err := getDomainInterfaces(*domain) if err != nil { diff --git a/libvirt/resource_libvirt_domain_test.go b/libvirt/resource_libvirt_domain_test.go index 86f78206..e8897f3d 100644 --- a/libvirt/resource_libvirt_domain_test.go +++ b/libvirt/resource_libvirt_domain_test.go @@ -351,6 +351,51 @@ func TestAccLibvirtDomain_Cpu(t *testing.T) { }) } +func TestAccLibvirtDomain_Filesystems(t *testing.T) { + var domain libvirt.Domain + + var config = fmt.Sprintf(` + resource "libvirt_domain" "acceptance-test-domain" { + name = "terraform-test" + filesystem { + source = "/tmp" + target = "tmp" + readonly = false + } + filesystem { + source = "/proc" + target = "proc" + readonly = true + } + }`) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckLibvirtDomainDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: config, + Check: resource.ComposeTestCheckFunc( + testAccCheckLibvirtDomainExists("libvirt_domain.acceptance-test-domain", &domain), + resource.TestCheckResourceAttr( + "libvirt_domain.acceptance-test-domain", "filesystem.0.source", "/tmp"), + resource.TestCheckResourceAttr( + "libvirt_domain.acceptance-test-domain", "filesystem.0.target", "tmp"), + resource.TestCheckResourceAttr( + "libvirt_domain.acceptance-test-domain", "filesystem.0.readonly", "0"), + resource.TestCheckResourceAttr( + "libvirt_domain.acceptance-test-domain", "filesystem.1.source", "/proc"), + resource.TestCheckResourceAttr( + "libvirt_domain.acceptance-test-domain", "filesystem.1.target", "proc"), + resource.TestCheckResourceAttr( + "libvirt_domain.acceptance-test-domain", "filesystem.1.readonly", "1"), + ), + }, + }, + }) +} + func testAccCheckLibvirtDomainDestroy(s *terraform.State) error { virtConn := testAccProvider.Meta().(*Client).libvirt |