Dmitry Ukov 2817df8482 Ensure node power state ON/OFF for Reset command
Change-Id: Icc6b18b4694a17ab2f8e0bf1112e41a1d18a5b00
Relates-To: #54
Depends-On: https://review.opendev.org/713622
2020-03-19 09:53:58 +04:00

144 lines
4.3 KiB
Go

package redfish
import (
"context"
"fmt"
"net/url"
"strings"
"time"
redfishApi "opendev.org/airship/go-redfish/api"
redfishClient "opendev.org/airship/go-redfish/client"
"opendev.org/airship/airshipctl/pkg/log"
)
const (
SystemRebootDelay = 2 * time.Second
SystemActionRetries = 30
RedfishURLSchemeSeparator = "+"
)
// Redfish Id ref is a URI which contains resource Id
// as the last part. This function extracts resource
// ID from ID ref
func GetResourceIDFromURL(refURL string) string {
u, err := url.Parse(refURL)
if err != nil {
log.Fatal(err)
}
trimmedURL := strings.TrimSuffix(u.Path, "/")
elems := strings.Split(trimmedURL, "/")
id := elems[len(elems)-1]
return id
}
// Checks whether an ID exists in Redfish IDref collection
func IsIDInList(idRefList []redfishClient.IdRef, id string) bool {
for _, r := range idRefList {
rID := GetResourceIDFromURL(r.OdataId)
if rID == id {
return true
}
}
return false
}
// This function walks through the list of manager's virtual media resources
// and gets the ID of virtualmedia which has type "CD" or "DVD"
func GetVirtualMediaID() (string, string, error) {
// TODO: Sushy emulator has a bug which sends 'virtualMedia.inserted' field as
// string instead of a boolean which causes the redfish client to fail
// while unmarshalling this field.
// Insert logic here after the bug is fixed in sushy-emulator.
return "Cd", "CD", nil
}
// This function walks through the bootsources of a system and sets the bootsource
// which is compatible with the given media type.
func SetSystemBootSourceForMediaType(ctx context.Context,
api redfishApi.RedfishAPI,
systemID string,
mediaType string) error {
/* Check available boot sources for system */
system, _, err := api.GetSystem(ctx, systemID)
if err != nil {
return ErrRedfishClient{Message: fmt.Sprintf("Get System[%s] failed with err: %v", systemID, err)}
}
allowableValues := system.Boot.BootSourceOverrideTargetRedfishAllowableValues
for _, bootSource := range allowableValues {
if strings.EqualFold(string(bootSource), mediaType) {
/* set boot source */
systemReq := redfishClient.ComputerSystem{}
systemReq.Boot.BootSourceOverrideTarget = bootSource
_, httpResp, err := api.SetSystem(ctx, systemID, systemReq)
return ScreenRedfishError(httpResp, err)
}
}
return ErrRedfishClient{Message: fmt.Sprintf("failed to set system[%s] boot source", systemID)}
}
// Reboots a system by force shutoff and turning on.
func RebootSystem(ctx context.Context, api redfishApi.RedfishAPI, systemID string) error {
waitForPowerState := func(desiredState redfishClient.PowerState) error {
// Check if number of retries is defined in context
totalRetries, ok := ctx.Value("numRetries").(int)
if !ok {
totalRetries = SystemActionRetries
}
for retry := 0; retry <= totalRetries; retry++ {
system, httpResp, err := api.GetSystem(ctx, systemID)
if err = ScreenRedfishError(httpResp, err); err != nil {
return err
}
if system.PowerState == desiredState {
return nil
}
time.Sleep(SystemRebootDelay)
}
return ErrOperationRetriesExceeded{}
}
resetReq := redfishClient.ResetRequestBody{}
// Send PowerOff request
resetReq.ResetType = redfishClient.RESETTYPE_FORCE_OFF
_, httpResp, err := api.ResetSystem(ctx, systemID, resetReq)
if err = ScreenRedfishError(httpResp, err); err != nil {
return err
}
// Check that node is powered off
if err = waitForPowerState(redfishClient.POWERSTATE_OFF); err != nil {
return err
}
// Send PowerOn request
resetReq.ResetType = redfishClient.RESETTYPE_ON
_, httpResp, err = api.ResetSystem(ctx, systemID, resetReq)
if err = ScreenRedfishError(httpResp, err); err != nil {
return err
}
// Check that node is powered on and return
return waitForPowerState(redfishClient.POWERSTATE_ON)
}
// Insert the remote virtual media to the given virtual media id.
// This assumes that isoPath is accessible to the redfish server and
// virtualMedia device is either of type CD or DVD.
func SetVirtualMedia(ctx context.Context,
api redfishApi.RedfishAPI,
managerID string,
vMediaID string,
isoPath string) error {
vMediaReq := redfishClient.InsertMediaRequestBody{}
vMediaReq.Image = isoPath
vMediaReq.Inserted = true
_, httpResp, err := api.InsertVirtualMedia(ctx, managerID, vMediaID, vMediaReq)
return ScreenRedfishError(httpResp, err)
}