Skip to content

Commit 31bec5a

Browse files
authored
Add xenstore support to the xenorchestra_vm resource (#295)
* Add xenstore support to the xenorchestra_vm resource Signed-off-by: Dom Del Nano <[email protected]> * Regenerate vm resource and data source documentation Signed-off-by: Dom Del Nano <[email protected]> * Ensure that the xenstore key is omitted from state if it wasn't specified in the tf code Signed-off-by: Dom Del Nano <[email protected]> * Fix tf doc generation Signed-off-by: Dom Del Nano <[email protected]> --------- Signed-off-by: Dom Del Nano <[email protected]>
1 parent 67d73fa commit 31bec5a

File tree

7 files changed

+208
-9
lines changed

7 files changed

+208
-9
lines changed

client/vm.go

+21-5
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,12 @@ type Vm struct {
125125
ResourceSet *FlatResourceSet `json:"resourceSet"`
126126
// TODO: (#145) Uncomment this once issues with secure_boot have been figured out
127127
// SecureBoot bool `json:"secureBoot,omitempty"`
128-
Tags []string `json:"tags"`
129-
Videoram Videoram `json:"videoram,omitempty"`
130-
Vga string `json:"vga,omitempty"`
131-
StartDelay int `json:startDelay,omitempty"`
132-
Host string `json:"$container"`
128+
Tags []string `json:"tags"`
129+
Videoram Videoram `json:"videoram,omitempty"`
130+
Vga string `json:"vga,omitempty"`
131+
StartDelay int `json:startDelay,omitempty"`
132+
Host string `json:"$container"`
133+
XenstoreData map[string]interface{} `json:"xenStoreData,omitempty"`
133134

134135
// These fields are used for passing in disk inputs when
135136
// creating Vms, however, this is not a real field as far
@@ -357,6 +358,17 @@ func (c *Client) CreateVm(vmReq Vm, createTime time.Duration) (*Vm, error) {
357358
return nil, err
358359
}
359360

361+
xsParams := map[string]interface{}{
362+
"id": vmId,
363+
"xenStoreData": vmReq.XenstoreData,
364+
}
365+
var success bool
366+
err = c.Call("vm.set", xsParams, &success)
367+
368+
if err != nil {
369+
return nil, err
370+
}
371+
360372
bootAfterCreate := params["bootAfterCreate"].(bool)
361373
if !bootAfterCreate && vmReq.PowerState == RunningPowerState {
362374
err = c.StartVm(vmId)
@@ -431,6 +443,10 @@ func (c *Client) UpdateVm(vmReq Vm) (*Vm, error) {
431443
params["resourceSet"] = vmReq.ResourceSet
432444
}
433445

446+
if len(vmReq.XenstoreData) > 0 {
447+
params["xenStoreData"] = vmReq.XenstoreData
448+
}
449+
434450
vga := vmReq.Vga
435451
if vga != "" {
436452
params["vga"] = vga

docs/data-sources/vms.md

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Read-Only:
8585
- `vga` (String)
8686
- `videoram` (Number)
8787
- `wait_for_ip` (Boolean)
88+
- `xenstore` (Map of String)
8889

8990
<a id="nestedobjatt--vms--disk"></a>
9091
### Nested Schema for `vms.disk`

docs/index.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,14 @@ provider "xenorchestra" {
4646
<!-- schema generated by tfplugindocs -->
4747
## Schema
4848

49+
### Required
50+
51+
- `password` (String) Password for xoa api. Can be set via the XOA_PASSWORD environment variable.
52+
- `url` (String) Hostname of the xoa router. Can be set via the XOA_URL environment variable.
53+
- `username` (String) User account for xoa api. Can be set via the XOA_USER environment variable.
54+
4955
### Optional
5056

5157
- `insecure` (Boolean) Whether SSL should be verified or not. Can be set via the XOA_INSECURE environment variable.
52-
- `password` (String) Password for xoa api. Can be set via the XOA_PASSWORD environment variable.
5358
- `retry_max_time` (String) If `retry_mode` is set, this specifies the duration for which the backoff method will continue retries. Can be set via the `XOA_RETRY_MAX_TIME` environment variable
5459
- `retry_mode` (String) Specifies if retries should be attempted for requests that require eventual . Can be set via the XOA_RETRY_MODE environment variable.
55-
- `url` (String) Hostname of the xoa router. Can be set via the XOA_URL environment variable.
56-
- `username` (String) User account for xoa api. Can be set via the XOA_USER environment variable.

docs/resources/vm.md

+8
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ resource "xenorchestra_vm" "bar" {
8989
timeouts {
9090
create = "20m"
9191
}
92+
93+
// Note: Xen Orchestra populates values within Xenstore and will need ignored via
94+
// lifecycle ignore_changes or modeled in your terraform code
95+
xenstore = {
96+
key1 = "val1"
97+
key2 = "val2"
98+
}
9299
}
93100
94101
# vm resource that uses wait_for_ip
@@ -170,6 +177,7 @@ $ xo-cli xo.getAllObjects filter='json:{"id": "cf7b5d7d-3cd5-6b7c-5025-5c935c8cd
170177
- `vga` (String) The video adapter the VM should use. Possible values include std and cirrus.
171178
- `videoram` (Number) The videoram option the VM should use. Possible values include 1, 2, 4, 8, 16
172179
- `wait_for_ip` (Boolean) Whether terraform should wait until IP addresses are present on the VM's network interfaces before considering it created. This only works if guest-tools are installed in the VM. Defaults to false.
180+
- `xenstore` (Map of String) The key value pairs to be populated in xenstore.
173181

174182
### Read-Only
175183

examples/resources/xenorchestra_vm/resource.tf

+7
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ resource "xenorchestra_vm" "bar" {
5858
timeouts {
5959
create = "20m"
6060
}
61+
62+
// Note: Xen Orchestra populates values within Xenstore and will need ignored via
63+
// lifecycle ignore_changes or modeled in your terraform code
64+
xenstore = {
65+
key1 = "val1"
66+
key2 = "val2"
67+
}
6168
}
6269

6370
# vm resource that uses wait_for_ip

xoa/resource_xenorchestra_vm.go

+48-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"regexp"
1010
"sort"
1111
"strconv"
12+
"strings"
1213
"time"
1314

1415
"github.com/ddelnano/terraform-provider-xenorchestra/client"
@@ -408,6 +409,14 @@ $ xo-cli xo.getAllObjects filter='json:{"id": "cf7b5d7d-3cd5-6b7c-5025-5c935c8cd
408409
},
409410
},
410411
},
412+
"xenstore": &schema.Schema{
413+
Type: schema.TypeMap,
414+
Optional: true,
415+
Description: "The key value pairs to be populated in xenstore.",
416+
Elem: &schema.Schema{
417+
Type: schema.TypeString,
418+
},
419+
},
411420
"tags": resourceTags(),
412421
}
413422
}
@@ -566,7 +575,8 @@ func resourceVmCreate(d *schema.ResourceData, m interface{}) error {
566575
Videoram: client.Videoram{
567576
Value: d.Get("videoram").(int),
568577
},
569-
Vga: d.Get("vga").(string),
578+
XenstoreData: d.Get("xenstore").(map[string]interface{}),
579+
Vga: d.Get("vga").(string),
570580
}
571581

572582
affinityHost := d.Get("affinity_host").(string)
@@ -927,6 +937,23 @@ func resourceVmUpdate(d *schema.ResourceData, m interface{}) error {
927937
vmReq.AffinityHost = &affinityHost
928938
}
929939

940+
if d.HasChange("xenstore") {
941+
xenstoreParams := map[string]interface{}{}
942+
o, n := d.GetChange("xenstore")
943+
oXs := o.(map[string]interface{})
944+
nXs := n.(map[string]interface{})
945+
946+
for k, _ := range oXs {
947+
xenstoreParams[k] = nil
948+
}
949+
950+
for k, v := range nXs {
951+
xenstoreParams[k] = v
952+
}
953+
954+
vmReq.XenstoreData = xenstoreParams
955+
}
956+
930957
haltPerformed := false
931958

932959
if haltForUpdates {
@@ -1185,10 +1212,30 @@ func recordToData(resource client.Vm, vifs []client.VIF, disks []client.Disk, cd
11851212
return err
11861213
}
11871214
}
1215+
if xenstore := d.Get("xenstore").(map[string]interface{}); len(xenstore) > 0 {
1216+
filtered := filterXenstoreDataToVmData(resource.XenstoreData)
1217+
if err := d.Set("xenstore", filtered); err != nil {
1218+
return err
1219+
}
1220+
}
11881221

11891222
return nil
11901223
}
11911224

1225+
func filterXenstoreDataToVmData(xenstore map[string]interface{}) map[string]interface{} {
1226+
filtered := map[string]interface{}{}
1227+
for key, value := range xenstore {
1228+
if strings.HasPrefix(key, "vm-data/") {
1229+
pieces := strings.SplitAfterN(key, "vm-data/", 2)
1230+
if len(pieces) != 2 {
1231+
continue
1232+
}
1233+
filtered[pieces[1]] = value
1234+
}
1235+
}
1236+
return filtered
1237+
}
1238+
11921239
func vmBlockedOperationsToList(v client.Vm) []string {
11931240
blockedOperations := []string{}
11941241
for k, _ := range v.BlockedOperations {

xoa/resource_xenorchestra_vm_test.go

+117
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,46 @@ func TestAccXenorchestraVm_createWhenWaitingForIp(t *testing.T) {
622622
})
623623
}
624624

625+
func TestAccXenorchestraVm_createAndUpdateXenstoreData(t *testing.T) {
626+
resourceName := "xenorchestra_vm.bar"
627+
vmName := fmt.Sprintf("%s - %s", accTestPrefix, t.Name())
628+
resource.ParallelTest(t, resource.TestCase{
629+
PreCheck: func() { testAccPreCheck(t) },
630+
Providers: testAccProviders,
631+
CheckDestroy: testAccCheckXenorchestraVmDestroy,
632+
Steps: []resource.TestStep{
633+
{
634+
Config: testAccVmConfigWithSingleXenstoreData(vmName),
635+
Check: resource.ComposeAggregateTestCheckFunc(
636+
testAccVmExists(resourceName),
637+
resource.TestCheckResourceAttrSet(resourceName, "id"),
638+
resource.TestCheckResourceAttr(resourceName, "xenstore.%", "2"),
639+
resource.TestCheckResourceAttr(resourceName, "xenstore.first", "value"),
640+
),
641+
},
642+
{
643+
Config: testAccVmConfigWithMultipleXenstoreData(vmName),
644+
Check: resource.ComposeAggregateTestCheckFunc(
645+
testAccVmExists(resourceName),
646+
resource.TestCheckResourceAttrSet(resourceName, "id"),
647+
resource.TestCheckResourceAttr(resourceName, "xenstore.first", "value"),
648+
resource.TestCheckResourceAttr(resourceName, "xenstore.second", "value"),
649+
),
650+
},
651+
{
652+
Config: testAccVmConfigWithSingleXenstoreData(vmName),
653+
Check: resource.ComposeAggregateTestCheckFunc(
654+
testAccVmExists(resourceName),
655+
resource.TestCheckResourceAttrSet(resourceName, "id"),
656+
resource.TestCheckResourceAttr(resourceName, "xenstore.%", "2"),
657+
resource.TestCheckResourceAttr(resourceName, "xenstore.first", "value"),
658+
resource.TestCheckNoResourceAttr(resourceName, "xenstore.second"),
659+
),
660+
},
661+
},
662+
})
663+
}
664+
625665
func TestAccXenorchestraVm_ensureVmsInResourceSetsCanBeUpdatedByNonAdminUsers(t *testing.T) {
626666
vmName := fmt.Sprintf("%s - %s", accTestPrefix, t.Name())
627667
adminUser := os.Getenv("XOA_USER")
@@ -2261,6 +2301,83 @@ resource "xenorchestra_vm" "bar" {
22612301
`, accDefaultNetwork.NameLabel, accTestPool.Id, vmName, accDefaultSr.Id)
22622302
}
22632303

2304+
func testAccVmConfigWithSingleXenstoreData(vmName string) string {
2305+
return testAccCloudConfigConfig(fmt.Sprintf("vm-template-%s", vmName), "template") + testAccTemplateConfig() + fmt.Sprintf(`
2306+
data "xenorchestra_network" "network" {
2307+
name_label = "%s"
2308+
pool_id = "%s"
2309+
}
2310+
2311+
resource "xenorchestra_vm" "bar" {
2312+
memory_max = 4295000000
2313+
wait_for_ip = true
2314+
cpus = 1
2315+
cloud_config = xenorchestra_cloud_config.bar.template
2316+
name_label = "%s"
2317+
name_description = "description"
2318+
template = data.xenorchestra_template.template.id
2319+
network {
2320+
network_id = data.xenorchestra_network.network.id
2321+
}
2322+
2323+
disk {
2324+
sr_id = "%s"
2325+
name_label = "disk 1"
2326+
size = 10001317888
2327+
}
2328+
2329+
xenstore = {
2330+
first = "value"
2331+
}
2332+
2333+
lifecycle {
2334+
ignore_changes = [
2335+
xenstore["mmio-hole-size"],
2336+
]
2337+
}
2338+
}
2339+
`, accDefaultNetwork.NameLabel, accTestPool.Id, vmName, accDefaultSr.Id)
2340+
}
2341+
2342+
func testAccVmConfigWithMultipleXenstoreData(vmName string) string {
2343+
return testAccCloudConfigConfig(fmt.Sprintf("vm-template-%s", vmName), "template") + testAccTemplateConfig() + fmt.Sprintf(`
2344+
data "xenorchestra_network" "network" {
2345+
name_label = "%s"
2346+
pool_id = "%s"
2347+
}
2348+
2349+
resource "xenorchestra_vm" "bar" {
2350+
memory_max = 4295000000
2351+
wait_for_ip = true
2352+
cpus = 1
2353+
cloud_config = xenorchestra_cloud_config.bar.template
2354+
name_label = "%s"
2355+
name_description = "description"
2356+
template = data.xenorchestra_template.template.id
2357+
network {
2358+
network_id = data.xenorchestra_network.network.id
2359+
}
2360+
2361+
disk {
2362+
sr_id = "%s"
2363+
name_label = "disk 1"
2364+
size = 10001317888
2365+
}
2366+
2367+
xenstore = {
2368+
first = "value"
2369+
second = "value"
2370+
}
2371+
2372+
lifecycle {
2373+
ignore_changes = [
2374+
xenstore["mmio-hole-size"],
2375+
]
2376+
}
2377+
}
2378+
`, accDefaultNetwork.NameLabel, accTestPool.Id, vmName, accDefaultSr.Id)
2379+
}
2380+
22642381
func testAccVmConfigWithDiskNameLabelAndNameDescription(vmName, nameLabel, description string) string {
22652382
return testAccCloudConfigConfig(fmt.Sprintf("vm-template-%s", vmName), "template") + testAccTemplateConfig() + fmt.Sprintf(`
22662383
data "xenorchestra_network" "network" {

0 commit comments

Comments
 (0)