Skip to content

Commit

Permalink
Support RAID and BIOS configuration for masters (#53)
Browse files Browse the repository at this point in the history
* Add dependency vendor packages

Signed-off-by: Hu Shuai <[email protected]>

* Add RAID and BIOS clean steps for master nodes of IPI

Signed-off-by: Hu Shuai <[email protected]>

* Abandon the use of AccessDetails here

Signed-off-by: Hu Shuai <[email protected]>

* Add unit test for func buildManualCleaningSteps

Signed-off-by: Hu Shuai <[email protected]>

* Use BMO's func: CheckRAIDInterface

Signed-off-by: Hu Shuai <[email protected]>

* Add field cleanSteps for struct provisionStateWorkflow

Signed-off-by: Hu Shuai <[email protected]>
  • Loading branch information
hs0210 authored Mar 22, 2022
1 parent 9e3f3f3 commit acfce98
Show file tree
Hide file tree
Showing 2,699 changed files with 705,005 additions and 17,455 deletions.
11 changes: 9 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@ module github.com/openshift-metal3/terraform-provider-ironic
go 1.16

require (
github.com/gophercloud/gophercloud v0.23.0
github.com/gophercloud/utils v0.0.0-20210909165623-d7085207ff6d
github.com/gophercloud/gophercloud v0.22.0
github.com/gophercloud/utils v0.0.0-20210720165645-8a3ad2ad9e70
github.com/hashicorp/go-retryablehttp v0.7.0
github.com/hashicorp/go-version v1.3.0
github.com/hashicorp/terraform-plugin-sdk v1.17.2
github.com/metal3-io/baremetal-operator v0.0.0-20220310151803-2b47127ed7ae
github.com/metal3-io/baremetal-operator/apis v0.0.0
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
)

replace (
github.com/metal3-io/baremetal-operator/apis => github.com/metal3-io/baremetal-operator/apis v0.0.0-20220310151803-2b47127ed7ae
github.com/metal3-io/baremetal-operator/pkg/hardwareutils => github.com/metal3-io/baremetal-operator/pkg/hardwareutils v0.0.0-20220310151803-2b47127ed7ae
)
425 changes: 408 additions & 17 deletions go.sum

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions ironic/resource_ironic_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ func resourceDeploymentCreate(d *schema.ResourceData, meta interface{}) error {
}

// Deploy the node - drive Ironic state machine until node is 'active'
return ChangeProvisionStateToTarget(client, nodeUUID, "active", &configDrive, deploySteps)
return ChangeProvisionStateToTarget(client, nodeUUID, "active", &configDrive, deploySteps, nil)
}

// fetchFullIgnition gets full igntion from the URL and cert passed to it and returns userdata as a string
Expand Down Expand Up @@ -301,5 +301,5 @@ func resourceDeploymentDelete(d *schema.ResourceData, meta interface{}) error {
return err
}

return ChangeProvisionStateToTarget(client, d.Id(), "deleted", nil, nil)
return ChangeProvisionStateToTarget(client, d.Id(), "deleted", nil, nil, nil)
}
120 changes: 111 additions & 9 deletions ironic/resource_ironic_node_v1.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ironic

import (
"encoding/json"
"fmt"
"log"
"time"
Expand All @@ -9,6 +10,8 @@ import (
"github.com/gophercloud/gophercloud/openstack/baremetal/v1/nodes"
"github.com/gophercloud/gophercloud/openstack/baremetal/v1/ports"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
metal3v1alpha1 "github.com/metal3-io/baremetal-operator/apis/metal3.io/v1alpha1"
"github.com/metal3-io/baremetal-operator/pkg/provisioner/ironic"
)

// Schema resource definition for an Ironic node.
Expand Down Expand Up @@ -174,6 +177,16 @@ func resourceNodeV1() *schema.Resource {
Optional: true,
Computed: true,
},
"raid_config": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"bios_settings": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
},
}
}
Expand Down Expand Up @@ -230,28 +243,37 @@ func resourceNodeV1Create(d *schema.ResourceData, meta interface{}) error {

// Make node manageable
if d.Get("manage").(bool) || d.Get("clean").(bool) || d.Get("inspect").(bool) {
if err := ChangeProvisionStateToTarget(client, d.Id(), "manage", nil, nil); err != nil {
if err := ChangeProvisionStateToTarget(client, d.Id(), "manage", nil, nil, nil); err != nil {
return fmt.Errorf("could not manage: %s", err)
}
}

// Clean node
if d.Get("clean").(bool) {
if err := ChangeProvisionStateToTarget(client, d.Id(), "clean", nil, nil); err != nil {
if err := setRAIDConfig(client, d); err != nil {
return fmt.Errorf("fail to set raid config: %s", err)
}

var cleanSteps []nodes.CleanStep
if cleanSteps, err = buildManualCleaningSteps(d.Get("raid_interface").(string), d.Get("raid_config").(string), d.Get("bios_settings").(string)); err != nil {
return fmt.Errorf("fail to build raid clean steps: %s", err)
}

if err := ChangeProvisionStateToTarget(client, d.Id(), "clean", nil, nil, cleanSteps); err != nil {
return fmt.Errorf("could not clean: %s", err)
}
}

// Inspect node
if d.Get("inspect").(bool) {
if err := ChangeProvisionStateToTarget(client, d.Id(), "inspect", nil, nil); err != nil {
if err := ChangeProvisionStateToTarget(client, d.Id(), "inspect", nil, nil, nil); err != nil {
return fmt.Errorf("could not inspect: %s", err)
}
}

// Make node available
if d.Get("available").(bool) {
if err := ChangeProvisionStateToTarget(client, d.Id(), "provide", nil, nil); err != nil {
if err := ChangeProvisionStateToTarget(client, d.Id(), "provide", nil, nil, nil); err != nil {
return fmt.Errorf("could not make node available: %s", err)
}
}
Expand Down Expand Up @@ -422,7 +444,7 @@ func resourceNodeV1Update(d *schema.ResourceData, meta interface{}) error {
if (d.HasChange("manage") && d.Get("manage").(bool)) ||
(d.HasChange("clean") && d.Get("clean").(bool)) ||
(d.HasChange("inspect") && d.Get("inspect").(bool)) {
if err := ChangeProvisionStateToTarget(client, d.Id(), "manage", nil, nil); err != nil {
if err := ChangeProvisionStateToTarget(client, d.Id(), "manage", nil, nil, nil); err != nil {
return fmt.Errorf("could not manage: %s", err)
}
}
Expand All @@ -436,21 +458,21 @@ func resourceNodeV1Update(d *schema.ResourceData, meta interface{}) error {

// Clean node
if d.HasChange("clean") && d.Get("clean").(bool) {
if err := ChangeProvisionStateToTarget(client, d.Id(), "clean", nil, nil); err != nil {
if err := ChangeProvisionStateToTarget(client, d.Id(), "clean", nil, nil, nil); err != nil {
return fmt.Errorf("could not clean: %s", err)
}
}

// Inspect node
if d.HasChange("inspect") && d.Get("inspect").(bool) {
if err := ChangeProvisionStateToTarget(client, d.Id(), "inspect", nil, nil); err != nil {
if err := ChangeProvisionStateToTarget(client, d.Id(), "inspect", nil, nil, nil); err != nil {
return fmt.Errorf("could not inspect: %s", err)
}
}

// Make node available
if d.HasChange("available") && d.Get("available").(bool) {
if err := ChangeProvisionStateToTarget(client, d.Id(), "provide", nil, nil); err != nil {
if err := ChangeProvisionStateToTarget(client, d.Id(), "provide", nil, nil, nil); err != nil {
return fmt.Errorf("could not make node available: %s", err)
}
}
Expand Down Expand Up @@ -481,7 +503,7 @@ func resourceNodeV1Delete(d *schema.ResourceData, meta interface{}) error {
return err
}

if err := ChangeProvisionStateToTarget(client, d.Id(), "deleted", nil, nil); err != nil {
if err := ChangeProvisionStateToTarget(client, d.Id(), "deleted", nil, nil, nil); err != nil {
return err
}

Expand Down Expand Up @@ -585,3 +607,83 @@ func changePowerState(client *gophercloud.ServiceClient, d *schema.ResourceData,

return nil
}

// setRAIDConfig calls ironic's API to send request to change a Node's RAID config.
func setRAIDConfig(client *gophercloud.ServiceClient, d *schema.ResourceData) (err error) {
var logicalDisks []nodes.LogicalDisk
var targetRAID *metal3v1alpha1.RAIDConfig

raidConfig := d.Get("raid_config").(string)
if raidConfig == "" {
return nil
}

err = json.Unmarshal([]byte(raidConfig), &targetRAID)
if err != nil {
return
}

err = ironic.CheckRAIDInterface(d.Get("raid_interface").(string), targetRAID)
if err != nil {
return
}

// Build target for RAID configuration steps
logicalDisks, err = ironic.BuildTargetRAIDCfg(targetRAID)
if len(logicalDisks) == 0 || err != nil {
return
}

// Set root volume
if len(d.Get("root_device").(map[string]interface{})) == 0 {
logicalDisks[0].IsRootVolume = new(bool)
*logicalDisks[0].IsRootVolume = true
} else {
log.Printf("rootDeviceHints is used, the first volume of raid will not be set to root")
}

// Set target for RAID configuration steps
return nodes.SetRAIDConfig(
client,
d.Id(),
nodes.RAIDConfigOpts{LogicalDisks: logicalDisks},
).ExtractErr()
}

// buildManualCleaningSteps builds the clean steps for RAID and BIOS configuration
func buildManualCleaningSteps(raidInterface, raidConfig, biosSetings string) (cleanSteps []nodes.CleanStep, err error) {
var targetRAID *metal3v1alpha1.RAIDConfig
var settings []map[string]string

if raidConfig != "" {
if err = json.Unmarshal([]byte(raidConfig), &targetRAID); err != nil {
return nil, err
}

// Build raid clean steps
raidCleanSteps, err := ironic.BuildRAIDCleanSteps(raidInterface, targetRAID, nil)
if err != nil {
return nil, err
}
cleanSteps = append(cleanSteps, raidCleanSteps...)
}

if biosSetings != "" {
if err = json.Unmarshal([]byte(biosSetings), &settings); err != nil {
return nil, err
}

cleanSteps = append(
cleanSteps,
nodes.CleanStep{
Interface: "bios",
Step: "apply_configuration",
Args: map[string]interface{}{
"settings": settings,
},
},
)
}

return
}
104 changes: 104 additions & 0 deletions ironic/resource_ironic_node_v1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package ironic

import (
"fmt"
"reflect"
"testing"

"github.com/gophercloud/gophercloud"
Expand Down Expand Up @@ -172,3 +173,106 @@ func testAccNodeResource(extraValue string) string {
%s
}`, extraValue)
}

func TestBuildManualCleaningSteps(t *testing.T) {
cases := []struct {
Scenario string
RAIDInterface string
RAIDConfig string
BIOSSettings string
Expected []nodes.CleanStep
ExpectedError bool
}{
{
Scenario: "no raid and no bios",
RAIDInterface: "no-raid",
RAIDConfig: "",
BIOSSettings: "",
Expected: nil,
},
{
Scenario: "just raid",
RAIDInterface: "irmc",
RAIDConfig: "{\"hardwareRAIDVolumes\":[{\"level\":\"0\",\"name\":\"raid0\"}],\"softwareRAIDVolumes\":null}",
BIOSSettings: "",
Expected: []nodes.CleanStep{
{
Interface: "raid",
Step: "delete_configuration",
},
{
Interface: "raid",
Step: "create_configuration",
},
},
},
{
Scenario: "just bios",
RAIDInterface: "irmc",
RAIDConfig: "",
BIOSSettings: "[{\"name\":\"cpu_vt_enabled\",\"value\":\"False\"},{\"name\":\"hyper_threading_enabled\",\"value\":\"True\"}]",
Expected: []nodes.CleanStep{
{
Interface: "bios",
Step: "apply_configuration",
Args: map[string]interface{}{
"settings": []map[string]string{
{
"name": "cpu_vt_enabled",
"value": "False",
},
{
"name": "hyper_threading_enabled",
"value": "True",
},
},
},
},
},
},
{
Scenario: "raid and bios",
RAIDInterface: "irmc",
RAIDConfig: "{\"hardwareRAIDVolumes\":[{\"level\":\"0\",\"name\":\"raid0\"}],\"softwareRAIDVolumes\":null}",
BIOSSettings: "[{\"name\":\"cpu_vt_enabled\",\"value\":\"False\"},{\"name\":\"hyper_threading_enabled\",\"value\":\"True\"}]",
Expected: []nodes.CleanStep{
{
Interface: "raid",
Step: "delete_configuration",
},
{
Interface: "raid",
Step: "create_configuration",
},
{
Interface: "bios",
Step: "apply_configuration",
Args: map[string]interface{}{
"settings": []map[string]string{
{
"name": "cpu_vt_enabled",
"value": "False",
},
{
"name": "hyper_threading_enabled",
"value": "True",
},
},
},
},
},
},
}

for _, c := range cases {
t.Run(c.Scenario, func(t *testing.T) {
step, err := buildManualCleaningSteps(c.RAIDInterface, c.RAIDConfig, c.BIOSSettings)
if !reflect.DeepEqual(c.Expected, step) {
t.Errorf("expected: %v, got: %v", c.Expected, step)
}
if (err != nil) != c.ExpectedError {
t.Errorf("got unexpected error: %v", err)
}
})
}
}
Loading

0 comments on commit acfce98

Please sign in to comment.