Skip to content

Commit

Permalink
Add support and tests for suspends and pauses
Browse files Browse the repository at this point in the history
  • Loading branch information
ddelnano committed Oct 31, 2023
1 parent df6c222 commit 65c5089
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 30 deletions.
2 changes: 2 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type XOClient interface {
DeleteVm(id string) error
HaltVm(id string) error
StartVm(id string) error
SuspendVm(id string) error
PauseVm(id string) error

GetCloudConfigByName(name string) ([]CloudConfig, error)
CreateCloudConfig(name, template string) (*CloudConfig, error)
Expand Down
63 changes: 37 additions & 26 deletions client/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,42 @@ func (v Vm) Compare(obj interface{}) bool {
return false
}

func (c *Client) SuspendVm(id string) error {
return c.changeVmState(id, "suspend", []string{SuspendedPowerState}, []string{RunningPowerState}, 2*time.Minute)
}

func (c *Client) changeVmState(id, action string, target, pending []string, timeout time.Duration) error {
// PV drivers are necessary for the XO api to issue a graceful shutdown.
// See https://github.com/terra-farm/terraform-provider-xenorchestra/issues/220
// for more details.
if err := c.waitForPVDriversDetected(id); err != nil {
return errors.New(
fmt.Sprintf("failed to gracefully %s vm (%s) since PV drivers were never detected", action, id))
}

params := map[string]interface{}{
"id": id,
}
var success bool
err := c.Call(fmt.Sprintf("vm.%s", action), params, &success)

if err != nil {
return err
}
return c.waitForVmState(
id,
StateChangeConf{
Pending: pending,
Target: target,
Timeout: timeout,
},
)
}

func (c *Client) PauseVm(id string) error {
return c.changeVmState(id, "pause", []string{PausedPowerState}, []string{RunningPowerState}, 2*time.Minute)
}

func (c *Client) CreateVm(vmReq Vm, createTime time.Duration) (*Vm, error) {
tmpl, err := c.GetTemplate(Template{
Id: vmReq.Template,
Expand Down Expand Up @@ -445,32 +481,7 @@ func (c *Client) StartVm(id string) error {
}

func (c *Client) HaltVm(id string) error {
// PV drivers are necessary for the XO api to issue a graceful shutdown.
// See https://github.com/terra-farm/terraform-provider-xenorchestra/issues/220
// for more details.
if err := c.waitForPVDriversDetected(id); err != nil {
return errors.New(
fmt.Sprintf("failed to gracefully halt vm (%s) since PV drivers were never detected", id))
}

params := map[string]interface{}{
"id": id,
}
var success bool
// TODO: This can block indefinitely before we get to the waitForVmHalt
err := c.Call("vm.stop", params, &success)

if err != nil {
return err
}
return c.waitForVmState(
id,
StateChangeConf{
Pending: []string{RunningPowerState},
Target: []string{HaltedPowerState},
Timeout: 2 * time.Minute,
},
)
return c.changeVmState(id, "stop", []string{HaltedPowerState}, []string{RunningPowerState}, 2*time.Minute)
}

func (c *Client) DeleteVm(id string) error {
Expand Down
18 changes: 14 additions & 4 deletions xoa/resource_xenorchestra_vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -909,17 +909,27 @@ func resourceVmUpdate(d *schema.ResourceData, m interface{}) error {
log.Printf("[DEBUG] powerStateChanged=%t newPowerState=%s\n", powerStateChanged, newPowerState)
if haltForUpdates || powerStateChanged {
switch newPowerState {
case client.PausedPowerState:
err := c.PauseVm(vmReq.Id)

if err != nil {
return err
}
case client.SuspendedPowerState:
err := c.SuspendVm(vmReq.Id)

if err != nil {
return err
}
case client.RunningPowerState:
err := c.StartVm(vmReq.Id)

if err != nil {
return err
}
case client.HaltedPowerState:
if haltPerformed {
// Nothing here since we are already halted
} else {

// If the VM wasn't halted as part of the update, perform the halt now
if !haltPerformed {
err := c.HaltVm(id)

if err != nil {
Expand Down
66 changes: 66 additions & 0 deletions xoa/resource_xenorchestra_vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,72 @@ func TestAccXenorchestraVm_createWithPowerStateChanges(t *testing.T) {
})
}

func TestAccXenorchestraVm_createAndSuspend(t *testing.T) {
resourceName := "xenorchestra_vm.bar"
vmName := fmt.Sprintf("%s - %s", accTestPrefix, t.Name())
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckXenorchestraVmDestroy,
Steps: []resource.TestStep{
{
Config: testAccVmConfigWithPowerState(vmName, client.RunningPowerState),
Check: resource.ComposeAggregateTestCheckFunc(
testAccVmExists(resourceName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttr(resourceName, "power_state", client.RunningPowerState)),
},
{
Config: testAccVmConfigWithPowerState(vmName, client.SuspendedPowerState),
Check: resource.ComposeAggregateTestCheckFunc(
testAccVmExists(resourceName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttr(resourceName, "power_state", client.SuspendedPowerState)),
},
{
Config: testAccVmConfigWithPowerState(vmName, client.RunningPowerState),
Check: resource.ComposeAggregateTestCheckFunc(
testAccVmExists(resourceName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttr(resourceName, "power_state", client.RunningPowerState)),
},
},
})
}

func TestAccXenorchestraVm_createAndPause(t *testing.T) {
resourceName := "xenorchestra_vm.bar"
vmName := fmt.Sprintf("%s - %s", accTestPrefix, t.Name())
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckXenorchestraVmDestroy,
Steps: []resource.TestStep{
{
Config: testAccVmConfigWithPowerState(vmName, client.RunningPowerState),
Check: resource.ComposeAggregateTestCheckFunc(
testAccVmExists(resourceName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttr(resourceName, "power_state", client.RunningPowerState)),
},
{
Config: testAccVmConfigWithPowerState(vmName, client.PausedPowerState),
Check: resource.ComposeAggregateTestCheckFunc(
testAccVmExists(resourceName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttr(resourceName, "power_state", client.PausedPowerState)),
},
{
Config: testAccVmConfigWithPowerState(vmName, client.RunningPowerState),
Check: resource.ComposeAggregateTestCheckFunc(
testAccVmExists(resourceName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttr(resourceName, "power_state", client.RunningPowerState)),
},
},
})
}

func TestAccXenorchestraVm_createAndPlanWithNonExistantVm(t *testing.T) {
resourceName := "xenorchestra_vm.bar"
vmName := fmt.Sprintf("%s - %s", accTestPrefix, t.Name())
Expand Down

0 comments on commit 65c5089

Please sign in to comment.