Skip to content

Commit

Permalink
Merge pull request #1 from ammmze/init
Browse files Browse the repository at this point in the history
feat: init stuff
  • Loading branch information
ammmze authored Dec 21, 2021
2 parents 02a8782 + 0af4e06 commit 30d70a6
Show file tree
Hide file tree
Showing 16 changed files with 796 additions and 1 deletion.
8 changes: 7 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ jobs:
go-version: 1.17

- name: Build
run: go build -v ./...
run: make

# Run vet & lint on the code
- name: Run vet & lint
run: |
go vet .
golint .
- name: Test
run: go test -v ./...
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@

# Dependency directories (remove the comment below to include it)
# vendor/

*.bak
local_test.go
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
build: install-tools generate-sources

install-tools:
go install $$(go list -f '{{join .Imports " "}}' tools.go)

generate-sources:
go generate ./...

compile-sources:
go build ./...
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,18 @@
# go-amt
Client library to interact with Intel AMT api (via wsman)


## Usage

```golang
connection := Connection{
Host: "192.168.32.6",
User: "admin",
Pass: "yourreallyawesomepassword",
}
client, err := NewClient(&connection)
assert.NoError(t, err)

err = client.PowerOn()
assert.NoError(t, err)
```
5 changes: 5 additions & 0 deletions amt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package amt

const (
resourceAMTBootSettingData = "http://intel.com/wbem/wscim/1/amt-schema/1/AMT_BootSettingData"
)
149 changes: 149 additions & 0 deletions boot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//go:generate stringer -type=bootConfigRole -trimprefix=bootConfigRole -linecomment

package amt

import (
"fmt"
"strconv"

"github.com/VictorLowther/simplexml/dom"
"github.com/VictorLowther/simplexml/search"
)

type bootConfigRole int

const (
bootConfigRoleIsNext bootConfigRole = 0
bootConfigRoleIsNextSingleUse bootConfigRole = 1
bootConfigRoleIsDefault bootConfigRole = 2
bootConfigRoleDMTFReserved bootConfigRole = 3 // 3..32767
bootConfigRoleVendorSpecified bootConfigRole = 32768 // 32768..65535
)

func setBootConfigRole(client *Client, role bootConfigRole) error {
bootConfigRef, err := getBootConfigSettingRef(client, "Intel(r) AMT: Boot Configuration 0")
if err != nil {
return err
}

message := client.wsManClient.Invoke(resourceCIMBootService, "SetBootConfigRole")
bootConfigSetting := message.MakeParameter("BootConfigSetting")
bootConfigSetting.AddChildren(bootConfigRef.Children()...)
message.AddParameter(bootConfigSetting)
message.Parameters("Role", strconv.Itoa(int(role)))

_, err = sendMessageForReturnValueInt(message)
if err != nil {
return err
}
return nil
}

func changeBootOrder(client *Client, items []string) error {
message := client.wsManClient.Invoke(resourceCIMBootConfigSetting, "ChangeBootOrder")

if len(items) > 0 {
// TODO: multiple?
pxeEndpointRef, err := getBootSourceRef(client, "Intel(r) AMT: Force PXE Boot")
if err != nil {
return err
}
sourceParam := message.MakeParameter("Source")
sourceParam.AddChildren(pxeEndpointRef.Children()...)
message.AddParameter(sourceParam)
}

_, err := sendMessageForReturnValueInt(message)
if err != nil {
return err
}
return nil
}

func getBootSettingData(client *Client) ([]*dom.Element, error) {
msg := client.wsManClient.Get(resourceAMTBootSettingData)
response, err := msg.Send()
if err != nil {
return nil, err
}
data := search.FirstTag("AMT_BootSettingData", resourceAMTBootSettingData, response.Body())
if data == nil {
return nil, fmt.Errorf("response was missing the AMT_BootSettingData")
}
return data.Children(), nil
}

func setBootSettingData(client *Client) error {
bootSettings, err := getBootSettingData(client)
if err != nil {
return err
}

settingsToKeep := []*dom.Element{}
for _, setting := range bootSettings {
switch setting.Name.Local {
// omit these ones ... they are read-only parameters (per meshcommand implementation)
case "WinREBootEnabled",
"UEFILocalPBABootEnabled",
"UEFIHTTPSBootEnabled",
"SecureBootControlEnabled",
"BootguardStatus",
"OptionsCleared",
"BIOSLastStatus",
"UefiBootParametersArray":
continue
// gonna make sure these are set to "false"
case "BIOSPause", "BIOSSetup":
setting.Content = []byte("false")
settingsToKeep = append(settingsToKeep, setting)
// gonna make sure these are set to "0"
case "BootMediaIndex":
setting.Content = []byte("0")
settingsToKeep = append(settingsToKeep, setting)
default:
settingsToKeep = append(settingsToKeep, setting)
}
}

msg := client.wsManClient.Put(resourceAMTBootSettingData)
data := dom.Elem("AMT_BootSettingData", resourceAMTBootSettingData)
data.AddChildren(settingsToKeep...)
msg.SetBody(data)
_, err = msg.Send()

return err
}

func setPXE(client *Client) error {
// clear existing boot order per meshcommander's implementation...
// "Set the boot order to null, this is needed for some AMT versions that don't clear this automatically."
// err := changeBootOrder(client, []string{})
// if err != nil {
// return err
// }

err := setBootSettingData(client)
if err != nil {
return err
}

err = setBootConfigRole(client, bootConfigRoleIsNextSingleUse)
if err != nil {
return err
}

err = changeBootOrder(client, []string{"Intel(r) AMT: Force PXE Boot"})
if err != nil {
return err
}

return nil
}

func getBootConfigSettingRef(client *Client, name string) (*dom.Element, error) {
return getEndpointReferenceByInstanceID(client, resourceCIMBootConfigSetting, name)
}

func getBootSourceRef(client *Client, name string) (*dom.Element, error) {
return getEndpointReferenceByInstanceID(client, resourceCIMBootSourceSetting, name)
}
36 changes: 36 additions & 0 deletions bootconfigrole_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 75 additions & 0 deletions cim.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package amt

import (
"fmt"
"strconv"

"github.com/VictorLowther/simplexml/dom"
"github.com/VictorLowther/simplexml/search"
"github.com/ammmze/wsman"
)

const (
resourceCIMAssociatedPowerManagementService = "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_AssociatedPowerManagementService"
resourceCIMBootConfigSetting = "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootConfigSetting"
resourceCIMBootService = "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootService"
resourceCIMBootSourceSetting = "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootSourceSetting"
resourceCIMPowerManagementService = "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService"
resourceCIMComputerSystem = "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem"
)

func getEndpointReferenceBySelector(client *Client, namespace string, selectorName string, selectorValue string) (*dom.Element, error) {
message := client.wsManClient.EnumerateEPR(namespace)

response, err := message.Send()
if err != nil {
return nil, err
}
items, err := response.EnumItems()
if err != nil {
return nil, err
}

for _, item := range items {
selector := search.First(search.Attr("Name", "*", selectorName), item.Descendants())
if selector == nil || string(selector.Content) != selectorValue {
continue
}
return item, nil
}
return nil, fmt.Errorf("could not find endpoint reference with selector %s=%s", selectorName, selectorValue)
}

func getEndpointReferenceByInstanceID(client *Client, namespace string, instanceID string) (*dom.Element, error) {
return getEndpointReferenceBySelector(client, namespace, "InstanceID", instanceID)
}

func getComputerSystemRef(client *Client, name string) (*dom.Element, error) {
return getEndpointReferenceBySelector(client, resourceCIMComputerSystem, "Name", name)
}

func getReturnValueInt(response *wsman.Message) (int, error) {
returnElement := search.FirstTag("ReturnValue", "*", response.AllBodyElements())
if returnElement == nil {
return -1, fmt.Errorf("no ReturnValue found in the response")
}
return strconv.Atoi(string(returnElement.Content))
}

func sendMessageForReturnValueInt(message *wsman.Message) (int, error) {
response, err := message.Send()
if err != nil {
return -1, err
}
returnValue, err := getReturnValueInt(response)

if err != nil {
return -1, err
}

if returnValue == 0 {
return returnValue, nil
}

return returnValue, fmt.Errorf("received invalid return value %d", returnValue)
}
63 changes: 63 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package amt

import (
"fmt"

"github.com/ammmze/wsman"
)

// Client used to perform actions on the machine
type Client struct {
wsManClient *wsman.Client
}

// NewClient creates an amt client to use.
func NewClient(connection Connection) (*Client, error) {
port := int(connection.Port)
if port == 0 {
port = 16992
}
path := connection.Path
if path == "" {
path = "/wsman"
}
target := fmt.Sprintf("http://%s:%d%s", connection.Host, port, path)
wsmanClient, err := wsman.NewClient(target, connection.User, connection.Pass, true)
if err != nil {
return nil, err
}
wsmanClient.Debug = connection.Debug
return &Client{
wsManClient: wsmanClient,
}, nil
}

// Close the client.
func (c *Client) Close() error {
return nil
}

// PowerOn will power on a given machine.
func (c *Client) PowerOn() error {
return powerOn(c)
}

// PowerOff will power off a given machine.
func (c *Client) PowerOff() error {
return powerOff(c)
}

// PowerCycle will power cycle a given machine.
func (c *Client) PowerCycle() error {
return powerCycle(c)
}

// SetPXE makes sure the node will pxe boot next time.
func (c *Client) SetPXE() error {
return setPXE(c)
}

// IsPoweredOn checks current power state.
func (c *Client) IsPoweredOn() (bool, error) {
return isPoweredOn(c)
}
11 changes: 11 additions & 0 deletions connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package amt

// Connection properties for a Client
type Connection struct {
Host string
Port int
Path string
User string
Pass string
Debug bool
}
Loading

0 comments on commit 30d70a6

Please sign in to comment.