diff --git a/config/config.yml b/config/config.yml index 12dc921e1..a450acc67 100644 --- a/config/config.yml +++ b/config/config.yml @@ -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 diff --git a/go.mod b/go.mod index e908a9054..f8b85dbf0 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index daf616cbc..435b3e7f8 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/internal/controller/httpapi/v1/devicemanagement.go b/internal/controller/httpapi/v1/devicemanagement.go index bd5f474b5..87014f392 100644 --- a/internal/controller/httpapi/v1/devicemanagement.go +++ b/internal/controller/httpapi/v1/devicemanagement.go @@ -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) } } + diff --git a/internal/controller/httpapi/v1/linkpreference.go b/internal/controller/httpapi/v1/linkpreference.go new file mode 100644 index 000000000..23605ca02 --- /dev/null +++ b/internal/controller/httpapi/v1/linkpreference.go @@ -0,0 +1,51 @@ +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") + // Handle no WiFi port error with 404 and error message + if errors.Is(err, wsman.ErrNoWiFiPort) { + c.JSON(http.StatusNotFound, gin.H{ + "error": "Set Link Preference failed: No WiFi port found for guid : " + guid + ".", + }) + return + } + // For other errors (device not found, validation, etc.), use standard error response + ErrorResponse(c, err) + return + } + + // Map AMT return value to HTTP status code + // Non-zero return value -> 400 Bad Request with error message + // 0 -> 200 OK with success response + if response.ReturnValue != 0 { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "Set Link Preference failed for guid : " + guid + ".", + }) + return + } + + c.JSON(http.StatusOK, response) +} diff --git a/internal/entity/dto/v1/linkpreference.go b/internal/entity/dto/v1/linkpreference.go new file mode 100644 index 000000000..7a99bf2c2 --- /dev/null +++ b/internal/entity/dto/v1/linkpreference.go @@ -0,0 +1,21 @@ +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" example:"0"` // Return code. 0 indicates success +} + +// LinkPreference enumeration values +const ( + LinkPreferenceME = 1 // Management Engine + LinkPreferenceHost = 2 // Host +) + +// Console-specific return value for no WiFi port found +const ReturnValueNoWiFiPort = -1 diff --git a/internal/usecase/devices/interfaces.go b/internal/usecase/devices/interfaces.go index c3441fd21..207df2508 100644 --- a/internal/usecase/devices/interfaces.go +++ b/internal/usecase/devices/interfaces.go @@ -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) } ) diff --git a/internal/usecase/devices/linkpreference.go b/internal/usecase/devices/linkpreference.go new file mode 100644 index 000000000..54a22fb7f --- /dev/null +++ b/internal/usecase/devices/linkpreference.go @@ -0,0 +1,38 @@ +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{ReturnValue: returnValue}, err + } + + return dto.LinkPreferenceResponse{ReturnValue: returnValue}, nil +} diff --git a/internal/usecase/devices/wsman/interfaces.go b/internal/usecase/devices/wsman/interfaces.go index cf345a66d..35295a012 100644 --- a/internal/usecase/devices/wsman/interfaces.go +++ b/internal/usecase/devices/wsman/interfaces.go @@ -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) } diff --git a/internal/usecase/devices/wsman/message.go b/internal/usecase/devices/wsman/message.go index b104bfc34..e13f5704d 100644 --- a/internal/usecase/devices/wsman/message.go +++ b/internal/usecase/devices/wsman/message.go @@ -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 @@ -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 +}