Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support event subscription and submitTestEvent for various vendors #187

Merged
merged 1 commit into from
Jul 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions oem/dell/dell.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// SPDX-License-Identifier: BSD-3-Clause
//

package dell

import (
"fmt"
"net/http"

"github.com/stmcginnis/gofish/redfish"
)

const eventContext string = "root"

var SubmitTestEventTarget = "/redfish/v1/EventService/Actions/EventService.SendTestEvent"

type (
PayloadType struct {
Destination string `json:"Destination"`
EventTypes string `json:"EventTypes"`
Context string `json:"Context"`
Protocol string `json:"Protocol"`
MessageID string `json:"MessageId"`
}
)

type EventService struct {
redfish.EventService
}

// SubmitTestEvent sends event according to msgId and returns error.
func (eventservice *EventService) SubmitTestEvent(messageID, eType, protocol string) error {
payload := PayloadType{
Destination: SubmitTestEventTarget,
EventTypes: eType,
Context: eventContext,
Protocol: protocol,
MessageID: messageID,
}
resp, err := eventservice.Client.Post(SubmitTestEventTarget, payload)

if err != nil {
return fmt.Errorf("failed to post submitTestEvent due to: %w", err)
}
defer resp.Body.Close()

valid := map[int]bool{
http.StatusNoContent: true,
http.StatusCreated: true}

if !valid[resp.StatusCode] {
return fmt.Errorf("on send event received response: %v due to: %s", resp.StatusCode, resp.Body)
}

return nil
}

func FromEventService(eventservice *redfish.EventService) (EventService, error) {
return EventService{*eventservice}, nil
}
237 changes: 237 additions & 0 deletions oem/dell/test/dell_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
//
// SPDX-License-Identifier: BSD-3-Clause
//

package test

import (
"encoding/json"
"log"
"net/http"
"net/http/httptest"
"testing"

"github.com/stmcginnis/gofish"
"github.com/stmcginnis/gofish/common"
"github.com/stmcginnis/gofish/oem/dell"
"github.com/stmcginnis/gofish/redfish"
)

const serviceRootBody = `{
"@odata.context": "/redfish/v1/$metadata#ServiceRoot.ServiceRoot",
"@odata.id": "/redfish/v1",
"@odata.type": "#ServiceRoot.v1_8_0.ServiceRoot",
"AccountService": {
"@odata.id": "/redfish/v1/AccountService"
},
"CertificateService": {
"@odata.id": "/redfish/v1/CertificateService"
},
"Chassis": {
"@odata.id": "/redfish/v1/Chassis"
},
"Description": "Root Service",
"EventService": {
"@odata.id": "/redfish/v1/EventService"
},
"Fabrics": {
"@odata.id": "/redfish/v1/Fabrics"
},
"Id": "RootService",
"JobService": {
"@odata.id": "/redfish/v1/JobService"
},
"JsonSchemas": {
"@odata.id": "/redfish/v1/JsonSchemas"
},
"Links": {
"Sessions": {
"@odata.id": "/redfish/v1/SessionService/Sessions"
}
},
"Managers": {
"@odata.id": "/redfish/v1/Managers"
},
"Name": "Root Service",
"Oem": {
"Dell": {
"@odata.context": "/redfish/v1/$metadata#DellServiceRoot.DellServiceRoot",
"@odata.type": "#DellServiceRoot.v1_0_0.DellServiceRoot",
"IsBranded": 0,
"ManagerMACAddress": "00:00:00:00:00:00",
"ServiceTag": "0000000"
}
},
"Product": "Integrated Dell Remote Access Controller",
"ProtocolFeaturesSupported": {
"DeepOperations": {
"DeepPATCH": false,
"DeepPOST": false
},
"ExcerptQuery": false,
"ExpandQuery": {
"ExpandAll": true,
"Levels": true,
"Links": true,
"MaxLevels": 1,
"NoLinks": true
},
"FilterQuery": true,
"OnlyMemberQuery": true,
"SelectQuery": true
},
"RedfishVersion": "1.11.0",
"Registries": {
"@odata.id": "/redfish/v1/Registries"
},
"SessionService": {
"@odata.id": "/redfish/v1/SessionService"
},
"Systems": {
"@odata.id": "/redfish/v1/Systems"
},
"Tasks": {
"@odata.id": "/redfish/v1/TaskService"
},
"TelemetryService": {
"@odata.id": "/redfish/v1/TelemetryService"
},
"UpdateService": {
"@odata.id": "/redfish/v1/UpdateService"
},
"Vendor": "Dell"
}`

const eventServiceBody = `{
"@odata.context": "/redfish/v1/$metadata#EventService.EventService",
"@odata.id": "/redfish/v1/EventService",
"@odata.type": "#EventService.v1_7_0.EventService",
"Actions": {
"#EventService.SubmitTestEvent": {
"EventType@Redfish.AllowableValues": ["Alert"],
"target": "/redfish/v1/EventService/Actions/EventService.SubmitTestEvent"
}
},
"DeliveryRetryAttempts": 3,
"DeliveryRetryIntervalSeconds": 5,
"Description": "Event Service represents the properties for the service",
"EventFormatTypes": ["Event", "MetricReport"],
"EventFormatTypes@odata.count": 2,
"EventTypesForSubscription": ["Alert", "MetricReport", "Other"],
"EventTypesForSubscription@odata.count": 3,
"Id": "EventService",
"Name": "Event Service",
"SMTP": {
"Authentication": "None",
"ConnectionProtocol": "StartTLS",
"FromAddress": "",
"Password": null,
"Port": 25,
"ServerAddress": "0.0.0.0",
"Username": ""
},
"SSEFilterPropertiesSupported": {
"EventFormatType": true,
"EventType": true,
"MessageId": true,
"MetricReportDefinition": true,
"OriginResource": true,
"RegistryPrefix": true,
"ResourceType": true,
"SubordinateResources": false
},
"ServerSentEventUri": "/redfish/v1/SSE",
"ServiceEnabled": true,
"Status": {
"Health": "OK",
"HealthRollup": "OK",
"State": "Enabled"
},
"Subscriptions": {
"@odata.id": "/redfish/v1/EventService/Subscriptions"
}
}`

func TestDellSubmitTestEvent(t *testing.T) {
const redfishBaseURL = "/redfish/v1/"
var (
c common.Client
err error
requestCounter int // this counter is used to verify that the received requests, are in the expected order
)

// Start a local HTTP server
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if req.Method == http.MethodGet &&
req.URL.String() == redfishBaseURL &&
requestCounter < 2 { // ServiceRoot
log.Printf("Mock received login request")
contentType := req.Header.Get("Content-Type")
if contentType != "application/json" {
t.Errorf("gofish connect sent wrong header. Content-Type:"+
" is %v and not expected application/json", contentType)
}

requestCounter++

// Send response to be tested
rw.WriteHeader(http.StatusOK)
rw.Header().Set("Content-Type", "application/json")

rw.Write([]byte(serviceRootBody)) // nolint:errcheck
} else if req.Method == http.MethodGet && // Get event service
req.URL.String() == "/redfish/v1/EventService" &&
requestCounter == 2 {
log.Printf("Getting event service")

requestCounter++

rw.Write([]byte(eventServiceBody)) // nolint:errcheck
} else if req.Method == http.MethodPost && // SubmitTestEvent
req.URL.String() == dell.SubmitTestEventTarget &&
requestCounter == 3 {
log.Printf("Mock got SubmitTestEvent POST")

err := json.NewDecoder(req.Body).Decode(&dell.PayloadType{})
if err != nil {
t.Errorf("error in SubmitTestEvent payload for Dell due to: %v", err)
}

requestCounter++

rw.WriteHeader(http.StatusCreated)
} else {
t.Errorf("mock got unexpected %v request to path %v while request counter is %v",
req.Method, req.URL.String(), requestCounter)
}
}))
// Close the server when test finishes
defer server.Close()

c, err = gofish.Connect(gofish.ClientConfig{Endpoint: server.URL, HTTPClient: server.Client()})

if err != nil {
t.Errorf("failed to establish client to mock http server due to: %v", err)
}

serviceRoot, err := gofish.ServiceRoot(c)
if err != nil {
t.Errorf("failed to get redfish service root due to: %v", err)
}
eventService, err := serviceRoot.EventService()
if err != nil {
t.Errorf("failed to get event service due to: %v", err)
}
dellEventService, err := dell.FromEventService(eventService)
if err != nil {
t.Errorf("failed to get dell event service due to: %v", err)
}

err = dellEventService.SubmitTestEvent(
"AMP0300",
"Alert",
string(redfish.RedfishEventDestinationProtocol))
if err != nil {
t.Errorf("failed to submit test event due to: %v", err)
}
}
65 changes: 65 additions & 0 deletions oem/hpe/events/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// SPDX-License-Identifier: BSD-3-Clause
//

package events

import (
"fmt"
"net/http"
"time"

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

var (
SubmitTestEventTarget = "/redfish/v1/EventService/Actions/EventService.SendTestEvent"
)

type PayloadType struct {
EventID string `json:"EventID"`
EventTimestamp string `json:"EventTimestamp"`
EventType string `json:"EventType"`
Message string
MessageArgs []string
MessageID string `json:"MessageId"`
OriginOfCondition string
Severity string
}

// SubmitTestEvent sends event according to msgId and returns error
// more info https://hewlettpackard.github.io/iLOAmpPack-Redfish-API-Docs/#submitting-a-test-event
func SubmitTestEvent(client common.Client, eventID, msgID, eventType, message string) error {
const condition = "/redfish/v1/Systems/1/"
const severity = "OK"

var messageArgs = []string{"NoAMS", "Busy", "Cached"}

payload := PayloadType{
EventID: eventID,
EventTimestamp: time.Now().Format(time.RFC3339), // "2019-07-29T15:13:49Z",
EventType: eventType, // redfish.SupportedEventTypes["Alert"],
Message: message,
MessageArgs: messageArgs,
MessageID: msgID,
OriginOfCondition: condition,
Severity: severity,
}
resp, err := client.Post(SubmitTestEventTarget, payload)

if err != nil {
return fmt.Errorf("failed to send submitTestEvent due to: %w", err)
}
defer resp.Body.Close()

valid := map[int]bool{
http.StatusOK: true,
http.StatusNoContent: true,
http.StatusCreated: true}

if !valid[resp.StatusCode] {
return fmt.Errorf("on send event received response: %v due to: %s", resp.StatusCode, resp.Body)
}

return nil
}
Loading