Skip to content
Draft
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
26 changes: 13 additions & 13 deletions platform-manageability-agent/internal/comms/comms.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ func (cli *Client) RetrieveActivationDetails(ctx context.Context, hostID string,
}
if strings.Contains(outputStr, `msg="Unable to authenticate with AMT"`) {
log.Logger.Warnf("Unable to authenticate with AMT for host %s - triggering deactivation", hostID)
cli.triggerDeactivationAsync(hostID, rpsAddress, password)
cli.triggerDeactivationAsync(hostID, rpsAddress, resp.ProfileName, password)
}

if activationErr != nil {
Expand All @@ -325,7 +325,7 @@ func (cli *Client) RetrieveActivationDetails(ctx context.Context, hostID string,
case "connecting":
log.Logger.Infof("host %s is in 'connecting' state", hostID)
rpsAddress := fmt.Sprintf("wss://%s/activate", conf.RPSAddress)
activationStatus = cli.handleConnectingState(hostID, normalizedStatus, rpsAddress, resp.ActionPassword)
activationStatus = cli.handleConnectingState(hostID, normalizedStatus, rpsAddress, resp.ProfileName, resp.ActionPassword)

case "connected":
// Update previous state and reset connecting state timestamp
Expand All @@ -351,7 +351,7 @@ func (cli *Client) RetrieveActivationDetails(ctx context.Context, hostID string,
// handleConnectingState manages the connecting state
// If device went directly to "connecting" (bypassing "not connected"), trigger immediate deactivation
// Otherwise, wait for 3 minutes before triggering deactivation
func (cli *Client) handleConnectingState(hostID, currentState, rpsAddress, password string) pb.ActivationStatus {
func (cli *Client) handleConnectingState(hostID, currentState, rpsAddress, profileName, password string) pb.ActivationStatus {
now := time.Now()

// Track when first entered connecting state
Expand All @@ -372,7 +372,7 @@ func (cli *Client) handleConnectingState(hostID, currentState, rpsAddress, passw
log.Logger.Infof("Host %s: direct transition to 'connecting' from '%s', triggering deactivation",
hostID, previousState)
// trigger the deactivation
return cli.triggerDeactivationAsync(hostID, rpsAddress, password)
return cli.triggerDeactivationAsync(hostID, rpsAddress, profileName, password)
}
} else {
cli.mu.Unlock()
Expand All @@ -392,15 +392,15 @@ func (cli *Client) handleConnectingState(hostID, currentState, rpsAddress, passw
log.Logger.Warnf("Host %s stuck in 'connecting' state for %v (>%v), triggering deactivation",
hostID, timeInConnecting, connectingStateTimeout)
// trigger the deactivation
return cli.triggerDeactivationAsync(hostID, rpsAddress, password)
return cli.triggerDeactivationAsync(hostID, rpsAddress, profileName, password)
}

// Still in connecting state within timeout
return pb.ActivationStatus_ACTIVATING
}

// triggerDeactivationAsync launches deactivation in background goroutine and returns immediately
func (cli *Client) triggerDeactivationAsync(hostID, rpsAddress, password string) pb.ActivationStatus {
func (cli *Client) triggerDeactivationAsync(hostID, rpsAddress, profileName, password string) pb.ActivationStatus {
cli.mu.Lock()
// Only trigger deactivation if not already in progress
if cli.deactivationInProgress {
Expand All @@ -415,14 +415,14 @@ func (cli *Client) triggerDeactivationAsync(hostID, rpsAddress, password string)
cli.mu.Unlock()

// Launch goroutine for deactivation
go cli.performDeactivationAsync(hostID, rpsAddress, password)
go cli.performDeactivationAsync(hostID, rpsAddress, profileName, password)
// Return ACTIVATION_FAILED to stop current activation attempts
return pb.ActivationStatus_ACTIVATION_FAILED
}

// performDeactivationAsync executes deactivation
// and polls RAS status until "not connected" or timeout
func (cli *Client) performDeactivationAsync(hostID, rpsAddress, password string) {
func (cli *Client) performDeactivationAsync(hostID, rpsAddress, profileName, password string) {
log.Logger.Infof("Starting async deactivation for host %s", hostID)

// Always reset deactivation in progress flag when done
Expand All @@ -433,7 +433,7 @@ func (cli *Client) performDeactivationAsync(hostID, rpsAddress, password string)
}()

// Execute deactivation command
deactivateOutput, deactivateErr := cli.Executor.ExecuteAMTDeactivate(rpsAddress, password)
deactivateOutput, deactivateErr := cli.Executor.ExecuteAMTDeactivate(rpsAddress, profileName, password)
if deactivateErr != nil {
log.Logger.Errorf("Deactivation command failed for host %s: %v, Output: %s",
hostID, deactivateErr, string(deactivateOutput))
Expand Down Expand Up @@ -576,10 +576,10 @@ func (cli *Client) GetDeactivationInProgress() bool {
return cli.deactivationInProgress
}

func (cli *Client) TriggerDeactivationAsync(hostID, rpsAddress, password string) pb.ActivationStatus {
return cli.triggerDeactivationAsync(hostID, rpsAddress, password)
func (cli *Client) TriggerDeactivationAsync(hostID, rpsAddress, profileName, password string) pb.ActivationStatus {
return cli.triggerDeactivationAsync(hostID, rpsAddress, profileName, password)
}

func (cli *Client) PerformDeactivationAsync(hostID, rpsAddress, password string) {
cli.performDeactivationAsync(hostID, rpsAddress, password)
func (cli *Client) PerformDeactivationAsync(hostID, rpsAddress, profileName, password string) {
cli.performDeactivationAsync(hostID, rpsAddress, profileName, password)
}
10 changes: 5 additions & 5 deletions platform-manageability-agent/internal/comms/comms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (m *mockCommandExecutor) ExecuteAMTActivate(rpsAddress, profileName, passwo
return m.activationOutput, m.activationError
}

func (m *mockCommandExecutor) ExecuteAMTDeactivate(rpsAddress, password string) ([]byte, error) {
func (m *mockCommandExecutor) ExecuteAMTDeactivate(rpsAddress, profileName, password string) ([]byte, error) {
return m.deactivationOutput, m.deactivationError
}

Expand Down Expand Up @@ -718,7 +718,7 @@ func TestTriggerDeactivationAsync_AlreadyInProgress(t *testing.T) {
client.SetDeactivationInProgress(true)

// Trigger deactivation - should be blocked
status := client.TriggerDeactivationAsync("host-id", "wss://mock-rps/activate", "test-password")
status := client.TriggerDeactivationAsync("host-id", "wss://mock-rps/activate", "test-profile", "test-password")
assert.Equal(t, pb.ActivationStatus_ACTIVATING, status, "Should return ACTIVATING when deactivation already in progress")
}

Expand Down Expand Up @@ -749,7 +749,7 @@ func TestPerformDeactivationAsync_Success(t *testing.T) {
// Perform deactivation
done := make(chan bool, 1) // Buffered channel to prevent goroutine leak
go func() {
client.PerformDeactivationAsync("host-id", "wss://mock-rps/activate", "test-password")
client.PerformDeactivationAsync("host-id", "wss://mock-rps/activate", "test-profile", "test-password")
done <- true
}()

Expand Down Expand Up @@ -784,7 +784,7 @@ func TestPerformDeactivationAsync_DeactivationCommandFails(t *testing.T) {
// Perform async deactivation
done := make(chan bool, 1) // Buffered channel to prevent goroutine leak
go func() {
client.PerformDeactivationAsync("host-id", "wss://mock-rps/activate", "test-password")
client.PerformDeactivationAsync("host-id", "wss://mock-rps/activate", "test-profile", "test-password")
done <- true
}()

Expand Down Expand Up @@ -828,7 +828,7 @@ func TestPerformDeactivationAsync_AMTInfoFailures(t *testing.T) {
// Perform deactivation
done := make(chan bool, 1) // Buffered channel to prevent goroutine leak
go func() {
client.PerformDeactivationAsync("host-id", "wss://mock-rps/activate", "test-password")
client.PerformDeactivationAsync("host-id", "wss://mock-rps/activate", "test-profile", "test-password")
done <- true
}()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (m *mockCommandExecutorForErrorHandling) ExecuteAMTActivate(rpsAddress, pro
return m.amtActivateOutput, m.amtActivateError
}

func (m *mockCommandExecutorForErrorHandling) ExecuteAMTDeactivate(rpsAddress, password string) ([]byte, error) {
func (m *mockCommandExecutorForErrorHandling) ExecuteAMTDeactivate(rpsAddress, profileName, password string) ([]byte, error) {
m.deactivationTriggered = true
m.deactivateCallCount++
return m.amtDeactivateOutput, m.amtDeactivateError
Expand Down
26 changes: 21 additions & 5 deletions platform-manageability-agent/internal/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
type CommandExecutor interface {
ExecuteAMTInfo() ([]byte, error)
ExecuteAMTActivate(rpsAddress, profileName, password string) ([]byte, error)
ExecuteAMTDeactivate(rpsAddress, password string) ([]byte, error)
ExecuteAMTDeactivate(rpsAddress, profileName, password string) ([]byte, error)
}

type RealCommandExecutor struct{}
Expand Down Expand Up @@ -83,13 +83,29 @@ func (r *RealCommandExecutor) ExecuteAMTActivate(rpsAddress, profileName, passwo
}

// ExecuteAMTDeactivate executes the AMT deactivate command and verifies success.
func (r *RealCommandExecutor) ExecuteAMTDeactivate(rpsAddress, password string) ([]byte, error) {
// It checks the profile name suffix to determine the appropriate deactivation method:
// - CCM (Client Control Mode) profile: uses local deactivation (sudo /usr/bin/rpc deactivate -local)
// - ACM (Admin Control Mode) profile: uses remote deactivation via RPS server
func (r *RealCommandExecutor) ExecuteAMTDeactivate(rpsAddress, profileName, password string) ([]byte, error) {
// Check if agent is being run with the DM manager mock and skip command if so
_, testCheck := os.LookupEnv("PMA_BINARY_PATH")
if !testCheck {
// Execute deactivation command
cmd := exec.Command("sudo", "-E", "/usr/bin/rpc", "deactivate", "-u", rpsAddress, "-n")
cmd.Env = append(cmd.Environ(), fmt.Sprintf("AMT_PASSWORD=%s", password))
// Determine deactivation mode based on profile name suffix
lowerProfile := strings.ToLower(profileName)
log.Logger.Infof("Determining deactivation mode from profile name: %s", profileName)

var cmd *exec.Cmd
if strings.HasSuffix(lowerProfile, "ccm") {
// CCM mode - use local deactivation
log.Logger.Infof("Profile indicates CCM mode, using local deactivation command")
cmd = exec.Command("sudo", "/usr/bin/rpc", "deactivate", "-local")
} else {
// ACM mode (or unknown) - use remote deactivation via RPS
log.Logger.Infof("Profile indicates ACM mode, using remote deactivation command")
cmd = exec.Command("sudo", "-E", "/usr/bin/rpc", "deactivate", "-u", rpsAddress, "-n")
cmd.Env = append(cmd.Environ(), fmt.Sprintf("AMT_PASSWORD=%s", password))
}

output, err := cmd.CombinedOutput()
outputStr := string(output)
log.Logger.Infof("AMT deactivate command output: %s", outputStr)
Expand Down