Skip to content
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 config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ app:
name: console
repo: device-management-toolkit/console
version: DEVELOPMENT
encryption_key: ""
encryption_key: "base64encodedkey12345678901234567890123456789012"
allow_insecure_ciphers: false
http:
host: localhost
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module github.com/device-management-toolkit/console

go 1.25

// replace github.com/device-management-toolkit/go-wsman-messages/v2 => ../go-wsman-messages
replace github.com/device-management-toolkit/go-wsman-messages/v2 => ../go-wsman-messages

require (
github.com/Masterminds/squirrel v1.5.4
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/device-management-toolkit/go-wsman-messages/v2 v2.35.0 h1:J5bQ12tjspC9MT5FORsykBXPcnGDB2p4cZME9zeIE3U=
github.com/device-management-toolkit/go-wsman-messages/v2 v2.35.0/go.mod h1:qlR1/nrV6QPse0918YSJ2bjMY8VNkm6qnNU0ioqQEsQ=
github.com/dhui/dktest v0.4.6 h1:+DPKyScKSEp3VLtbMDHcUq6V5Lm5zfZZVb0Sk7Ahom4=
github.com/dhui/dktest v0.4.6/go.mod h1:JHTSYDtKkvFNFHJKqCzVzqXecyv+tKt8EzceOmQOgbU=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
Expand Down
4 changes: 4 additions & 0 deletions internal/controller/httpapi/v1/devicemanagement.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,9 @@ func NewAmtRoutes(handler *gin.RouterGroup, d devices.Feature, amt amtexplorer.F
// KVM display settings
h.GET("kvm/displays/:guid", r.getKVMDisplays)
h.PUT("kvm/displays/:guid", r.setKVMDisplays)

// Network link preference
h.POST("network/linkPreference/:guid", r.setLinkPreference)
}
}

45 changes: 45 additions & 0 deletions internal/controller/httpapi/v1/linkpreference.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package v1

import (
"errors"
"net/http"

"github.com/gin-gonic/gin"

"github.com/device-management-toolkit/console/internal/entity/dto/v1"
"github.com/device-management-toolkit/console/internal/usecase/devices/wsman"
)

// setLinkPreference sets the link preference (ME or Host) on a device's WiFi port.
func (r *deviceManagementRoutes) setLinkPreference(c *gin.Context) {
guid := c.Param("guid")

var req dto.LinkPreferenceRequest
if err := c.ShouldBindJSON(&req); err != nil {
ErrorResponse(c, err)

return
}

response, err := r.d.SetLinkPreference(c.Request.Context(), guid, req)
if err != nil {
r.l.Error(err, "http - v1 - setLinkPreference")
// Map specific errors to HTTP status codes (matching MPS implementation)
if errors.Is(err, wsman.ErrNoWiFiPort) {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
ErrorResponse(c, err)

return
}

// Map AMT return value to HTTP status code (matching MPS implementation)
// MPS logic: null -> 404, -1 or non-zero -> 400, 0 -> 200
httpStatus := http.StatusOK
if response.ReturnValue == -1 || response.ReturnValue != 0 {
httpStatus = http.StatusBadRequest
}

c.JSON(httpStatus, response)
}
64 changes: 64 additions & 0 deletions internal/entity/dto/v1/linkpreference.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package dto

// LinkPreferenceRequest represents the request to set link preference on a device.
type LinkPreferenceRequest struct {
LinkPreference int `json:"linkPreference" binding:"required,min=1,max=2"` // 1 for ME, 2 for HOST
Timeout int `json:"timeout" binding:"required,min=0"` // Timeout in seconds
}

// LinkPreferenceResponse represents the response from setting link preference.
type LinkPreferenceResponse struct {
ReturnValue int `json:"returnValue"`
ReturnValueStr string `json:"returnValueStr"`
}

// LinkPreference enumeration values
const (
LinkPreferenceME = 1 // Management Engine
LinkPreferenceHost = 2 // Host
)

// Return value constants for SetLinkPreference
const (
ReturnValueSuccess = 0
ReturnValueNotSupported = 1
ReturnValueUnknownFailed = 2
ReturnValueTimeout = 3
ReturnValueFailed = 4
ReturnValueInvalidParameter = 5
ReturnValueInUse = 6
ReturnValueTransitionStarted = 4096
ReturnValueInvalidStateTransition = 4097
ReturnValueTimeoutParameterNotSupport = 4098
ReturnValueBusy = 4099
)

// GetReturnValueString returns a human-readable string for the return value.
func GetReturnValueString(returnValue int) string {
switch returnValue {
case ReturnValueSuccess:
return "SUCCESS"
case ReturnValueNotSupported:
return "NOT_SUPPORTED"
case ReturnValueUnknownFailed:
return "UNKNOWN_FAILED"
case ReturnValueTimeout:
return "TIMEOUT"
case ReturnValueFailed:
return "FAILED"
case ReturnValueInvalidParameter:
return "INVALID_PARAMETER"
case ReturnValueInUse:
return "IN_USE"
case ReturnValueTransitionStarted:
return "TRANSITION_STARTED"
case ReturnValueInvalidStateTransition:
return "INVALID_STATE_TRANSITION"
case ReturnValueTimeoutParameterNotSupport:
return "TIMEOUT_PARAMETER_NOT_SUPPORT"
case ReturnValueBusy:
return "BUSY"
default:
return "UNKNOWN"
}
}
2 changes: 2 additions & 0 deletions internal/usecase/devices/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,7 @@ type (
// KVM Screen Settings (IPS_ScreenSettingData)
GetKVMScreenSettings(c context.Context, guid string) (dto.KVMScreenSettings, error)
SetKVMScreenSettings(c context.Context, guid string, req dto.KVMScreenSettingsRequest) (dto.KVMScreenSettings, error)
// Link Preference (AMT_EthernetPortSettings)
SetLinkPreference(c context.Context, guid string, req dto.LinkPreferenceRequest) (dto.LinkPreferenceResponse, error)
}
)
41 changes: 41 additions & 0 deletions internal/usecase/devices/linkpreference.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package devices

import (
"context"

dto "github.com/device-management-toolkit/console/internal/entity/dto/v1"
)

// SetLinkPreference sets the link preference (ME or Host) on a device's WiFi port.
func (uc *UseCase) SetLinkPreference(c context.Context, guid string, req dto.LinkPreferenceRequest) (dto.LinkPreferenceResponse, error) {
item, err := uc.repo.GetByID(c, guid, "")
if err != nil {
return dto.LinkPreferenceResponse{}, err
}

if item == nil || item.GUID == "" {
return dto.LinkPreferenceResponse{}, ErrNotFound
}

// Validate link preference value
if req.LinkPreference != dto.LinkPreferenceME && req.LinkPreference != dto.LinkPreferenceHost {
return dto.LinkPreferenceResponse{}, ErrValidationUseCase.Wrap("SetLinkPreference", "validate link preference", "linkPreference must be 1 (ME) or 2 (Host)")
}

// Validate timeout
if req.Timeout < 0 {
return dto.LinkPreferenceResponse{}, ErrValidationUseCase.Wrap("SetLinkPreference", "validate timeout", "timeout must be non-negative")
}

device, _ := uc.device.SetupWsmanClient(*item, false, true)

returnValue, err := device.SetLinkPreference(req.LinkPreference, req.Timeout)
if err != nil {
return dto.LinkPreferenceResponse{}, err
}

return dto.LinkPreferenceResponse{
ReturnValue: returnValue,
ReturnValueStr: dto.GetReturnValueString(returnValue),
}, nil
}
1 change: 1 addition & 0 deletions internal/usecase/devices/wsman/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,5 @@ type Management interface {
GetIPSKVMRedirectionSettingData() (kvmredirection.Response, error)
SetIPSKVMRedirectionSettingData(data *kvmredirection.KVMRedirectionSettingsRequest) (kvmredirection.Response, error)
DeleteCertificate(instanceID string) error
SetLinkPreference(linkPreference, timeout int) (int, error)
}
55 changes: 55 additions & 0 deletions internal/usecase/devices/wsman/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,22 @@ var (

// ErrCIRADeviceNotConnected is returned when a CIRA device is not connected or not found.
ErrCIRADeviceNotConnected = errors.New("CIRA device not connected/not found")
// ErrWsmanMessage is used for wrapping wsman message errors.
ErrWsmanMessage = &wsmanError{}
// ErrNoWiFiPort is returned when no WiFi port is found on the device.
ErrNoWiFiPort = errors.New("no WiFi port found (PhysicalConnectionType=3)")
)

type wsmanError struct{}

func (e *wsmanError) Error() string {
return "wsman message error"
}

func (e *wsmanError) Wrap(operation, context, message string) error {
return errors.New(operation + ": " + context + ": " + message)
}

type ConnectionEntry struct {
WsmanMessages wsman.Messages
IsCIRA bool
Expand Down Expand Up @@ -1917,3 +1931,44 @@ func (c *ConnectionEntry) GetIPSKVMRedirectionSettingData() (kvmredirection.Resp
func (c *ConnectionEntry) SetIPSKVMRedirectionSettingData(req *kvmredirection.KVMRedirectionSettingsRequest) (kvmredirection.Response, error) {
return c.WsmanMessages.IPS.KVMRedirectionSettingData.Put(req)
}

// SetLinkPreference sets the link preference (ME or Host) on the WiFi port.
// linkPreference: 1 for ME, 2 for Host
// timeout: timeout in seconds
// Returns the return value from the AMT device or an error.
func (c *ConnectionEntry) SetLinkPreference(linkPreference, timeout int) (int, error) {
// Get all ethernet port settings to find WiFi port
enumResponse, err := c.WsmanMessages.AMT.EthernetPortSettings.Enumerate()
if err != nil {
return -1, err
}

pullResponse, err := c.WsmanMessages.AMT.EthernetPortSettings.Pull(enumResponse.Body.EnumerateResponse.EnumerationContext)
if err != nil {
return -1, err
}

// Find WiFi port (PhysicalConnectionType = 3)
var wifiInstanceID string

for i := range pullResponse.Body.PullResponse.EthernetPortItems {
port := &pullResponse.Body.PullResponse.EthernetPortItems[i]
// PhysicalConnectionType: 3 = Wireless LAN
if port.PhysicalConnectionType == 3 {
wifiInstanceID = port.InstanceID
break
}
}

if wifiInstanceID == "" {
return -1, ErrNoWiFiPort
}

// Call SetLinkPreference on the WiFi port
response, err := c.WsmanMessages.AMT.EthernetPortSettings.SetLinkPreference(linkPreference, timeout, wifiInstanceID)
if err != nil {
return -1, err
}

return response.Body.SetLinkPreferenceResponse.ReturnValue, nil
}