Skip to content
This repository was archived by the owner on May 10, 2024. It is now read-only.
Open
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
2 changes: 1 addition & 1 deletion chassis.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func (r *ChassisStatusResponse) UnmarshalBinary(buf []byte) error {
}
r.CompletionCode = CompletionCode(buf[0])
r.PowerState = buf[1]
r.LastPowerEvent = buf[2]
r.LastPowerEvent = buf[2]
r.State = buf[3]
if len(buf) > 4 {
r.FrontControlPanel = buf[4]
Expand Down
45 changes: 45 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ limitations under the License.

package ipmi

import (
"bytes"
)

// Client provides common high level functionality around the underlying transport
type Client struct {
*Connection
Expand Down Expand Up @@ -118,3 +122,44 @@ func (c *Client) Control(ctl ChassisControl) error {
}
return c.Send(r, &ChassisControlResponse{})
}

func (c *Client) GetMcId() (*DcmiGetMcIdResponse, error) {
index := uint8(0)
lastChunkReceived := false
var dataBuffer bytes.Buffer
res := &DcmiGetMcIdResponse{}

// Read until the last chunk len is less than MAX_MC_ID_STRING_LEN
for lastChunkReceived != true {
r := &Request{
NetworkFunctionDcmi,
CommandGetMcIdString,
&DcmiGetMcIdRequest{DCMI_GROUP_EXTENSION_ID, index, MAX_MC_ID_STRING_LEN},
}

if err := c.Send(r, res); err != nil {
return res, err
}

// Accumulate the bytes received in the index until it
// is the same as the number of bytes expected
dataBuffer.WriteString(res.Data)
index += uint8(len(res.Data))
if res.NumBytes == index {
res.Data = dataBuffer.String()
lastChunkReceived = true
}
}

return res, nil
}

func (c *Client) SetMcId(mcId string) (*DcmiSetMcIdResponse, error) {
r := &Request{
NetworkFunctionDcmi,
CommandSetMcIdString,
&DcmiSetMcIdRequest{DCMI_GROUP_EXTENSION_ID, 0, uint8(len(mcId)), mcId},
}
res := &DcmiSetMcIdResponse{}
return res, c.Send(r, res)
}
2 changes: 2 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const (
CommandChassisStatus = Command(0x01)
CommandSetSystemBootOptions = Command(0x08)
CommandGetSystemBootOptions = Command(0x09)
CommandGetMcIdString = Command(0x09)
CommandSetMcIdString = Command(0x0a)
)

// Request structure
Expand Down
130 changes: 130 additions & 0 deletions dcmi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package ipmi

const (
MAX_MC_ID_STRING_LEN = 16
DCMI_GROUP_EXTENSION_ID = 0xDC
)

// DcmiGetMcIdRequest per section 6.4.6.1
type DcmiGetMcIdRequest struct {
GroupExtensionId uint8
Offset uint8
NumBytes uint8
}

// DcmiGetMcIdResponse per section 6.4.6.1
type DcmiGetMcIdResponse struct {
CompletionCode
GroupExtensionId uint8
NumBytes uint8
Data string
}

// DcmiSetMcIdRequest per section 6.4.6.2
type DcmiSetMcIdRequest struct {
GroupExtensionId uint8
Offset uint8
NumBytes uint8
Data string
}

// DcmiSetMcIdResponse per section 6.4.6.2
type DcmiSetMcIdResponse struct {
CompletionCode
GroupExtensionId uint8
LastOffsetWritten uint8
}

// MarshalBinary implementation to handle variable length Data
func (r *DcmiGetMcIdRequest) MarshalBinary() ([]byte, error) {
buf := make([]byte, 3)
buf[0] = r.GroupExtensionId
buf[1] = r.Offset
buf[2] = r.NumBytes
return buf, nil
}

// UnmarshalBinary implementation to handle variable length Data
func (r *DcmiGetMcIdRequest) UnmarshalBinary(buf []byte) error {
if len(buf) < 3 {
return ErrShortPacket
}
r.GroupExtensionId = buf[0]
r.Offset = buf[1]
r.NumBytes = buf[2]
return nil
}

// MarshalBinary implementation to handle variable length Data
func (r *DcmiGetMcIdResponse) MarshalBinary() ([]byte, error) {
buf := make([]byte, 3+len(r.Data))
buf[0] = byte(r.CompletionCode)
buf[1] = r.GroupExtensionId
buf[2] = r.NumBytes
copy(buf[3:], r.Data)
return buf, nil
}

// UnmarshalBinary implementation to handle variable length Data
func (r *DcmiGetMcIdResponse) UnmarshalBinary(buf []byte) error {
if len(buf) < 4 {
return ErrShortPacket
}
r.CompletionCode = CompletionCode(buf[0])
r.GroupExtensionId = buf[1]
r.NumBytes = buf[2]
ending := len(buf)
for idx, b := range buf[3:] {
if b == 0x00 {
ending = idx + 3
break
}
}
r.Data = string(buf[3:ending])
return nil
}

// MarshalBinary implementation to handle variable length Data
func (r *DcmiSetMcIdRequest) MarshalBinary() ([]byte, error) {
buf := make([]byte, 3+MAX_MC_ID_STRING_LEN)
buf[0] = r.GroupExtensionId
buf[1] = r.Offset
buf[2] = MAX_MC_ID_STRING_LEN
copy(buf[3:], r.Data)
for i := 3 + len(r.Data); i < MAX_MC_ID_STRING_LEN; i++ {
buf[i] = 0x00
}
return buf, nil
}

// UnmarshalBinary implementation to handle variable length Data
func (r *DcmiSetMcIdRequest) UnmarshalBinary(buf []byte) error {
if len(buf) < 4 {
return ErrShortPacket
}
r.GroupExtensionId = buf[0]
r.Offset = buf[1]
r.NumBytes = buf[2]
r.Data = string(buf[3 : 3+r.NumBytes])
return nil
}

// MarshalBinary implementation to handle variable length Data
func (r *DcmiSetMcIdResponse) MarshalBinary() ([]byte, error) {
buf := make([]byte, 3)
buf[0] = byte(r.CompletionCode)
buf[1] = r.GroupExtensionId
buf[2] = r.LastOffsetWritten
return buf, nil
}

// UnmarshalBinary implementation to handle variable length Data
func (r *DcmiSetMcIdResponse) UnmarshalBinary(buf []byte) error {
if len(buf) < 3 {
return ErrShortPacket
}
r.CompletionCode = CompletionCode(buf[0])
r.GroupExtensionId = buf[1]
r.LastOffsetWritten = buf[2]
return nil
}
44 changes: 44 additions & 0 deletions dcmi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ipmi

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestGetMcIdStringRequest(t *testing.T) {
req := &Request{
NetworkFunctionDcmi,
CommandGetMcIdString,
&DcmiGetMcIdRequest{DCMI_GROUP_EXTENSION_ID, 0, MAX_MC_ID_STRING_LEN},
}
raw := requestToStrings(req)
assert.Equal(t, []string{"0x2c", "0x09", "0xdc", "0x00", "0x10"}, raw)
}

func TestGetMcIdStringResponse(t *testing.T) {
status := &DcmiGetMcIdResponse{}
err := responseFromString("dc 0c 61 62 63 64 65 66 67 68 69 6a 6b 6c 00 00 00 00", status)
assert.NoError(t, err)
assert.Equal(t, status.GroupExtensionId, uint8(DCMI_GROUP_EXTENSION_ID))
assert.Equal(t, status.NumBytes, uint8(12))
assert.Equal(t, status.Data, "abcdefghijkl")
}

func TestSetMcIdStringRequest(t *testing.T) {
testMcId := "abcdefghijkl"
req := &Request{
NetworkFunctionDcmi,
CommandSetMcIdString,
&DcmiSetMcIdRequest{DCMI_GROUP_EXTENSION_ID, 0, uint8(len(testMcId)), testMcId},
}
raw := requestToStrings(req)
assert.Equal(t, []string{"0x2c", "0x0a", "0xdc", "0x00", "0x10", "0x61", "0x62", "0x63", "0x64", "0x65", "0x66", "0x67", "0x68", "0x69", "0x6a", "0x6b", "0x6c", "0x00", "0x00", "0x00", "0x00"}, raw)
}

func TestSetMcIdStringResponse(t *testing.T) {
status := &DcmiSetMcIdResponse{}
err := responseFromString("dc 0c 61 62 63 64 65 66 67 68 69 6a 6b 6c 00 00 00 00", status)
assert.NoError(t, err)
assert.Equal(t, status.GroupExtensionId, uint8(DCMI_GROUP_EXTENSION_ID))
assert.Equal(t, status.LastOffsetWrriten, uint8(0x0c))
}
8 changes: 3 additions & 5 deletions lan.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"crypto/rand"
"errors"
"fmt"
"io"
"log"
"net"
"os"
Expand Down Expand Up @@ -73,10 +74,7 @@ func (l *lan) open() error {

func (l *lan) close() error {
if l.active {
err := l.closeSession()
if err != nil {
log.Printf("error closing session: %s", err)
}
l.closeSession()
l.active = false
}

Expand All @@ -102,7 +100,7 @@ func (l *lan) send(req *Request, res Response) error {
return m.Response(res)
}

func (*lan) Console() error {
func (*lan) Console(_ io.Writer, _ io.Writer) error {
fmt.Println("Console not supported. Press Enter to continue.")
r := make([]byte, 1)
_, err := os.Stdin.Read(r)
Expand Down
1 change: 1 addition & 0 deletions message.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type NetworkFunction uint8
var (
NetworkFunctionChassis = NetworkFunction(0x00)
NetworkFunctionApp = NetworkFunction(0x06)
NetworkFunctionDcmi = NetworkFunction(0x2c)
)

var (
Expand Down
10 changes: 6 additions & 4 deletions tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"
"os/exec"
"strconv"
Expand Down Expand Up @@ -55,11 +56,11 @@ func (t *tool) send(req *Request, res Response) error {
return responseFromString(output, res)
}

func (t *tool) Console() error {
func (t *tool) Console(outWriter io.Writer, errWriter io.Writer) error {
cmd := t.cmd("sol", "activate", "-e", "&")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdout = outWriter
cmd.Stderr = errWriter
return cmd.Run()
}

Expand Down Expand Up @@ -133,7 +134,8 @@ func responseFromBytes(msg []byte, r Response) error {
}

func responseFromString(s string, r Response) error {
msg := rawDecode(strings.TrimSpace(s))
// Remove trailing spaces and newlines from string received.
msg := rawDecode(strings.Replace(strings.TrimSpace(s), "\n", "", -1))
return responseFromBytes(msg, r)
}

Expand Down
2 changes: 1 addition & 1 deletion tool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func TestTool(t *testing.T) {
err = tr.send(req, bor)
assert.NoError(t, err)
assert.Equal(t, uint8(BootParamBootFlags), bor.Param)
assert.Equal(t, uint8(BootDevicePxe), bor.BootDeviceSelector())
assert.Equal(t, BootDevicePxe, bor.BootDeviceSelector())
assert.Equal(t, uint8(0x40), bor.Data[1]&0x40)

// Invalid command
Expand Down
7 changes: 5 additions & 2 deletions transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ limitations under the License.

package ipmi

import "fmt"
import (
"fmt"
"io"
)

type transport interface {
open() error
close() error
send(*Request, Response) error
// Console enters Serial Over LAN mode
Console() error
Console(io.Writer, io.Writer) error
}

func newTransport(c *Connection) (transport, error) {
Expand Down
1 change: 1 addition & 0 deletions vendor/github.com/stretchr/testify
Submodule testify added at 6cb3b8