summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlavio Castelli <fcastelli@suse.com>2017-06-19 21:09:04 +0200
committerFlavio Castelli <fcastelli@suse.com>2017-06-19 21:09:04 +0200
commit4307ba55c5d4cabf750756f243bed97505f8e61a (patch)
tree2e449e111ea5ae5411ccb95534dc921917d242dd
parent1b6657408ec4259426d7420fe48ae97415428f3c (diff)
downloadterraform-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.markdown44
-rw-r--r--libvirt/domain_def.go54
-rw-r--r--libvirt/resource_libvirt_domain.go54
-rw-r--r--libvirt/resource_libvirt_domain_test.go45
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