Skip to content

Conversation

@mbuchmann-hpe
Copy link

Summary and Scope

Adds JAWS PDU Support:
ServerTech PDUs do not use Redfish, but instead uses the JSON API Web Service (JAWS).
Instead of creating an interface which converts Redfish calls to JAWS, it was decided to add support for PCS to call JAWS directly.
Added the file jaws.go which takes care of the JAWS calls and monitoring of the PDU.
The URIs must be setup correctly in SMD (HSM) to detect these PDUs and make the correct calls.

Testing

Tested locally using the SMD setup on Tamarindo and the PDU from another local system. Able to detect PDUs, query and store power status and power on/off outlets.

Test Procedure

Using a ServerTech PDU, add the PDU to SMD/HSM. PCS should automatically discover the PDU and start to monitor it for power status.

Test using the following curl (these assume PCS is running on localhost:28007 and the PDU is x3000m0)

  1. Check all power status: curl http://localhost:28007/v1/power-status | jq
  2. Check power status of an outlet (outlet 17): curl http://localhost:28007/v1/power-status?xname="x3000m0p0v17" | jq
  3. Turn an outlet off (outlet 17): curl http://localhost:28007/v1/transitions -d '{"location":[{"xname":"x3000m0p0v17"}],"operation":"off"}'
  4. Check the transaction (use transaction id from power off): curl http://localhost:28007/v1/transitions/6d9ba49b-ba21-4ec6-b2ab-440d12693230 | jq
  5. Review power status
  6. Turn outlet back on: curl http://localhost:28007/v1/transitions -d '{"location":[{"xname":"x3000m0p0v17"}],"operation":"on"}'

Risks and Mitigations

Changes where carefully done to leave all current functionality in tact.
Environment variables must be set to enable the PDU monitoring (see Dockerfile).

@bmcdonald3
Copy link
Collaborator

Testing:

PDU in SMD:

curl -sS http://localhost:27779/hsm/v2/Inventory/RedfishEndpoints | jq
{
  "RedfishEndpoints": [
    {
      "ID": "x3000m0",
      "Type": "CabinetPDUController",
      "Hostname": "x3000m0",
      "Domain": "",
      "FQDN": "x3000m0",
      "Enabled": true,
      "User": "",
      "Password": "",
      "RediscoverOnUpdate": false,
      "DiscoveryInfo": {
        "LastDiscoveryStatus": "NotYetQueried"
      }
    }
  ]
}

Power status:

curl -sS -X GET http://localhost:28007/v1/power-status?xname=x3000m0p0v17 | jq '.'
{
  "status": [
    {
      "xname": "x3000m0p0v17",
      "powerState": "on",
      "managementState": "available",
      "error": "",
      "supportedPowerTransitions": [
        "On",
        "Soft-Off",
        "Off",
        "Init",
        "Hard-Restart",
        "Soft-Restart"
      ],
      "lastUpdated": "2025-08-01T21:08:44.403327907Z"
    }
  ]
}

Power off:

curl -sS -X POST -H "Content-Type: application/json" -d '{"operation": "Off", "location": [{"xname": "x3000m0p0v17"}]}' http://localhost:28007/v1/transitions

Confirmed:

{
  "status": [
    {
      "xname": "x3000m0p0v17",
      "powerState": "off",
      "managementState": "available",
      "error": "",
      "supportedPowerTransitions": [
        "On",
        "Soft-Off",
        "Off",
        "Init",
        "Hard-Restart",
        "Soft-Restart"
      ],
      "lastUpdated": "2025-07-21T21:15:18.849195805Z"
    }
  ]
}

Just need those commits signed.

Copy link
Member

@alexlovelltroy alexlovelltroy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Two minor issues:

  1. I don't understand the need for the GODEBUG environment variable and am suspicious that we don't actually want it in the production container.

  2. The JAWS implementation appears to be fairly well compartmentalized. Can we move it to pkg so other tools can use it?

Copy link
Member

@alexlovelltroy alexlovelltroy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@rainest rainest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably separate concerns more, but if nothing else we should make sure we're handling all errors, and aborting on them immediately if we cannot expect to successfully proceed.

@bmcdonald3 bmcdonald3 mentioned this pull request Aug 19, 2025
Signed-off-by: Michael Buchmann <[email protected]>
@mbuchmann-hpe mbuchmann-hpe requested a review from rainest August 22, 2025 18:35
Copy link
Contributor

@rainest rainest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I missed the TLS stuff previously. Do these devices usually have decent enough cert management that we can default to secure and provide a flag to skip JAWS TLS verification? Can we also limit the additional ciphers to them?

func JawsLoad(xname string, FQDN string, authUser string, authPass string) {
timeout := 20
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed this earlier, hard coding this off seems iffy. What's the typical certificate management of these devices like? I'm not surprised if it's not great and winds up using self-signed certs often, but we should at add comments here indicating why this is off if so.

Ideally we actually default it on and make insecure an explicit opt-in.

We can also set

CipherSuites: []uint16{...}

here rather than setting tlsrakex=1, correct? Ideally we don't enable those across the board.

Unfortunately Golang's helper list functions return cipher suite structs while configuration wants their int representation, so we'd need our own helper to generate the longer list:

func jawsSuites() []uint16 {
    suites := []uint16{tls.TLS_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_RSA_WITH_AES_256_CBC_SHA}
    for _, c := range tls.CipherSuites() {
        suites = append(suites, c.ID)
    }
    return suites
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants