From 57cd7051484f5864c853dd91cb5c54d51b088b4f Mon Sep 17 00:00:00 2001 From: jagratac Date: Mon, 25 May 2026 06:01:03 +0000 Subject: [PATCH] updated deactivation for CCM mode --- .../internal/comms/comms.go | 26 +++++++++---------- .../internal/comms/comms_test.go | 10 +++---- .../internal/comms/error_handling_test.go | 2 +- .../internal/utils/utils.go | 26 +++++++++++++++---- 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/platform-manageability-agent/internal/comms/comms.go b/platform-manageability-agent/internal/comms/comms.go index bd105636..828d8979 100644 --- a/platform-manageability-agent/internal/comms/comms.go +++ b/platform-manageability-agent/internal/comms/comms.go @@ -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 { @@ -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 @@ -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 @@ -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() @@ -392,7 +392,7 @@ 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 @@ -400,7 +400,7 @@ func (cli *Client) handleConnectingState(hostID, currentState, rpsAddress, passw } // 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 { @@ -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 @@ -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)) @@ -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) } diff --git a/platform-manageability-agent/internal/comms/comms_test.go b/platform-manageability-agent/internal/comms/comms_test.go index bf48adf1..ef980268 100644 --- a/platform-manageability-agent/internal/comms/comms_test.go +++ b/platform-manageability-agent/internal/comms/comms_test.go @@ -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 } @@ -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") } @@ -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 }() @@ -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 }() @@ -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 }() diff --git a/platform-manageability-agent/internal/comms/error_handling_test.go b/platform-manageability-agent/internal/comms/error_handling_test.go index 4cdf996c..9abbfdfa 100644 --- a/platform-manageability-agent/internal/comms/error_handling_test.go +++ b/platform-manageability-agent/internal/comms/error_handling_test.go @@ -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 diff --git a/platform-manageability-agent/internal/utils/utils.go b/platform-manageability-agent/internal/utils/utils.go index 269f7ae3..f5e257aa 100644 --- a/platform-manageability-agent/internal/utils/utils.go +++ b/platform-manageability-agent/internal/utils/utils.go @@ -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{} @@ -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)