Thanks to visit codestin.com
Credit goes to github.com

Skip to content
3 changes: 3 additions & 0 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -727,4 +727,7 @@ type ErrExtendedInfo struct {
// ActionTarget is contains the target endpoint for object Actions.
type ActionTarget struct {
Target string
// ActionInfoTarget is an optional resource that provides information about parameters
// that are supported by the associated Target.
ActionInfoTarget string `json:"@Redfish.ActionInfo"`
}
61 changes: 61 additions & 0 deletions redfish/actioninfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// SPDX-License-Identifier: BSD-3-Clause
//

package redfish

import "github.com/stmcginnis/gofish/common"

// ActionInfoDataTypes is the datatype for an ActionInfo value.
type ActionInfoDataTypes string

const (
BooleanActionInfoDataTypes ActionInfoDataTypes = "Boolean"
NumberActionInfoDataTypes ActionInfoDataTypes = "Number"
NumberArrayActionInfoDataTypes ActionInfoDataTypes = "NumberArray"
ObjectActionInfoDataTypes ActionInfoDataTypes = "Object"
ObjectArrayActionInfoDataTypes ActionInfoDataTypes = "ObjectArray"
StringActionInfoDataTypes ActionInfoDataTypes = "String"
StringArrayActionInfoDataTypes ActionInfoDataTypes = "StringArray"
)

type ActionInfo struct {
common.Resource
// Parameters is the list of parameters included in the specified Redfish action.
// This property shall list the parameters included in the specified Redfish
// action for this resource.
Parameters []ActionInfoParameter
}

type ActionInfoParameter struct {
// AllowableNumbers are the allowable numeric values or duration values, inclusive ranges of values,
// and incremental step values for this parameter as applied to this action target.
AllowableNumbers []string
// AllowablePattern shall contain a regular expression that describes the allowable values for this
// parameter as applied to this action target.
AllowablePattern string
// AllowableValueDescriptions shall contain the descriptions of allowable values for this parameter.
AllowableValueDescriptions []string
// AllowableValues shall indicate the allowable values for this parameter as applied to this action target.
AllowableValues []string
// ArraySizeMaximum shall contain the maximum number of array elements that this service supports for this parameter.
ArraySizeMaximum uint64
// ArraySizeMinimum shall contain the minimum number of array elements required by this service for this parameter.
ArraySizeMinimum uint64
// DataType shall contain the JSON property type for this parameter.
DataType ActionInfoDataTypes
// MaximumValue integer or number property shall contain the maximum value that this service supports.
MaximumValue string
// MinimumValue integer or number property shall contain the minimum value that this service supports.
MinimumValue string
// Name shall contain the name of the parameter included in a Redfish action.
Name string
// ObjectDataType shall describe the entity type definition in @odata.type format for the parameter.
ObjectDataType string
// Required shall indicate whether the parameter is required to complete this action.
Required bool
}

func GetActionInfo(c common.Client, uri string) (*ActionInfo, error) {
return common.GetObject[ActionInfo](c, uri)
}
84 changes: 84 additions & 0 deletions redfish/actioninfo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// SPDX-License-Identifier: BSD-3-Clause
//

package redfish

import (
"encoding/json"
"strings"
"testing"
)

var actionInfoBody = `{
"@odata.type": "#ActionInfo.v1_4_2.ActionInfo",
"Id": "ResetActionInfo",
"Name": "Reset Action Info",
"Parameters": [
{
"Name": "ResetType",
"Required": true,
"DataType": "String",
"AllowableValues": [
"On",
"ForceOff"
]
}
],
"@odata.id": "/redfish/v1/Systems/1/ResetActionInfo"
}`

func TestActionInfo(t *testing.T) {
var result ActionInfo
err := json.NewDecoder(strings.NewReader(actionInfoBody)).Decode(&result)

if err != nil {
t.Errorf("Error decoding JSON: %s", err)
}

if result.ODataType != "#ActionInfo.v1_4_2.ActionInfo" {
t.Errorf("Received invalid ODataType: %s", result.ODataType)
}

if result.ID != "ResetActionInfo" {
t.Errorf("Received invalid ID: %s", result.ID)
}

if result.Name != "Reset Action Info" {
t.Errorf("Received invalid name: %s", result.Name)
}

if result.ODataID != "/redfish/v1/Systems/1/ResetActionInfo" {
t.Errorf("Received invalid ODataID: %s", result.ODataID)
}

if len(result.Parameters) != 1 {
t.Errorf("Received invalid number of parameters: %d", len(result.Parameters))
}

firstParam := result.Parameters[0]

if firstParam.Name != "ResetType" {
t.Errorf("Received invalid param name: %s", firstParam.Name)
}

if firstParam.Required != true {
t.Errorf("Received invalid param required value: %t", firstParam.Required)
}

if firstParam.DataType != StringActionInfoDataTypes {
t.Errorf("Received invalid param data type: %s", firstParam.DataType)
}

if len(firstParam.AllowableValues) != 2 {
t.Errorf("Received invalid number of allowable values: %d", len(firstParam.AllowableValues))
}

if firstParam.AllowableValues[0] != "On" {
t.Errorf("Received invalid allowable value [0]: %s", firstParam.AllowableValues[0])
}

if firstParam.AllowableValues[1] != "ForceOff" {
t.Errorf("Received invalid allowable value [1]: %s", firstParam.AllowableValues[1])
}
}
22 changes: 20 additions & 2 deletions redfish/logservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const (
// DiagnosticDataDetail is the detailed information for a supported CollectDiagnosticData action.
type DiagnosticDataDetail struct {
// DiagnosticDataType indicates the type of diagnostic data to collect.
DiagnosticDataType DiagnosticDataTypes
DiagnosticDataType LogDiagnosticDataTypes
// EstimatedDuration represents the estimated total time required to generate the data.
EstimatedDuration string
// EstimatedSizeBytes represents the estimated size of the data to be collected.
Expand Down Expand Up @@ -143,6 +143,8 @@ type LogService struct {

// collectDiagnosticDataTarget is the URL to send CollectDiagnosticData actions to. (v1.2+)
collectDiagnosticDataTarget string
// collectDiagnosticInfoTarget is the URL to get ActionInfo about the CollectDiagnosticData action.
collectDiagnosticInfoTarget string
}

// UnmarshalJSON unmarshals a LogService object from the raw JSON.
Expand All @@ -168,6 +170,7 @@ func (logservice *LogService) UnmarshalJSON(b []byte) error {
logservice.entries = t.Entries.String()
logservice.clearLogTarget = t.Actions.ClearLog.Target
logservice.collectDiagnosticDataTarget = t.Actions.CollectDiagnosticData.Target
logservice.collectDiagnosticInfoTarget = t.Actions.CollectDiagnosticData.ActionInfoTarget

// This is a read/write object, so we need to save the raw object data for later
logservice.rawData = b
Expand Down Expand Up @@ -220,6 +223,11 @@ func (logservice *LogService) FilteredEntries(options ...common.FilterOption) ([
return ListReferencedLogEntrys(logservice.GetClient(), fmt.Sprintf("%s%s", logservice.entries, filter))
}

// SupportsClearLog indicates if the ClearLog action is supported.
func (logservice *LogService) SupportsClearLog() bool {
return logservice.clearLogTarget != ""
}

// ClearLog shall delete all entries found in the Entries collection for this
// Log Service.
func (logservice *LogService) ClearLog() error {
Expand Down Expand Up @@ -257,7 +265,7 @@ func (logservice *LogService) ClearLog() error {

type CollectDiagnosticDataParameters struct {
// DiagnosticDataType (required) shall contain the type of diagnostic data to collect.
DiagnosticDataType DiagnosticDataTypes `json:",omitempty"`
DiagnosticDataType LogDiagnosticDataTypes `json:",omitempty"`
// OEMDiagnosticDataType (optional) shall contain the OEM-defined type of diagnostic data if the
// DiagnosticDataType is set to `OEMDiagnosticDataTypes`
OEMDiagnosticDataType string `json:",omitempty"`
Expand Down Expand Up @@ -299,3 +307,13 @@ func (logservice *LogService) CollectDiagnosticData(parameters *CollectDiagnosti

return "", nil
}

// For Redfish v1.2+
// CollectDiagnosticDataActionInfo, if supported, provides the ActionInfo for a CollectDiagnosticData action.
func (logservice *LogService) CollectDiagnosticDataActionInfo() (*ActionInfo, error) {
if logservice.collectDiagnosticInfoTarget == "" {
return nil, errors.New("CollectDiagnosticData ActionInfo not supported by this service")
}

return common.GetObject[ActionInfo](logservice.GetClient(), logservice.collectDiagnosticInfoTarget)
}
33 changes: 31 additions & 2 deletions redfish/logservice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ var logServiceBodyTmpl = `{

var logServiceBody = fmt.Sprintf(logServiceBodyTmpl, `
, "#LogService.CollectDiagnosticData": {
"target": "/redfish/v1/Managers/BMC/LogServices/Log/Actions/LogService.CollectDiagnosticData"
"target": "/redfish/v1/Managers/BMC/LogServices/Log/Actions/LogService.CollectDiagnosticData",
"@Redfish.ActionInfo": "/redfish/v1/Managers/BMC/LogServices/Log/CollectDiagnosticDataActionInfo"
}
`)
var logServiceBodyNoDiag = fmt.Sprintf(logServiceBodyTmpl, "")
Expand Down Expand Up @@ -92,6 +93,10 @@ func TestLogService(t *testing.T) {
if result.collectDiagnosticDataTarget != "/redfish/v1/Managers/BMC/LogServices/Log/Actions/LogService.CollectDiagnosticData" {
t.Errorf("Invalid CollectDiagnosticData target: %s", result.collectDiagnosticDataTarget)
}

if result.collectDiagnosticInfoTarget != "/redfish/v1/Managers/BMC/LogServices/Log/CollectDiagnosticDataActionInfo" {
t.Errorf("Invalid CollectDiagnosticData ActionInfo target: %s", result.collectDiagnosticInfoTarget)
}
}

func initLogServiceClient(t *testing.T, template string) (*LogService, *common.TestClient) {
Expand Down Expand Up @@ -163,7 +168,7 @@ func TestLogServiceCollectDiagnosticsDataSuccess(t *testing.T) {
}}

location, err := logSvc.CollectDiagnosticData(&CollectDiagnosticDataParameters{
DiagnosticDataType: ManagerDiagnosticDataTypes,
DiagnosticDataType: ManagerLogDiagnosticDataTypes,
})
if err != nil {
t.Errorf("Error triggering diagnostic data: %s", err)
Expand All @@ -180,3 +185,27 @@ func TestLogServiceCollectionDiagnosticsDataUnsupported(t *testing.T) {
t.Errorf("log service unexpectedly supports diagnostic data")
}
}

func TestLogServiceCollectDiagnosticsActionInfo(t *testing.T) {
logSvc, testClient := initLogServiceClient(t, logServiceBody)

testClient.CustomReturnForActions = map[string][]interface{}{
http.MethodGet: {
&http.Response{
StatusCode: http.StatusOK,
// just the example ActionInfo from DSP0268 6.3.4
Body: io.NopCloser(strings.NewReader(actionInfoBody)),
},
}}

actionInfo, err := logSvc.CollectDiagnosticDataActionInfo()
if err != nil {
t.Errorf("Error getting diagnostic action info: %s", err)
}

if actionInfo.ODataType != "#ActionInfo.v1_4_2.ActionInfo" {
t.Errorf("Invalid action info type: %s", actionInfo.ODataType)
}

// not thoroughly testing the ActionInfo parsing - that will be handled in its own unit test
}
4 changes: 4 additions & 0 deletions redfish/powersupplyunit.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,7 @@ func (powerSupplyUnit *PowerSupplyUnit) PowerOutlets() ([]*Outlet, error) {
func (powerSupplyUnit *PowerSupplyUnit) PoweringChassis() ([]*Chassis, error) {
return common.GetObjects[Chassis](powerSupplyUnit.GetClient(), powerSupplyUnit.poweringChassis)
}

func (powerSupplyUnit *PowerSupplyUnit) RawData() []byte {
return powerSupplyUnit.rawData
}