Skip to content

Commit 85deee8

Browse files
committed
tests and changes
Signed-off-by: Abhishek Kumar <[email protected]>
1 parent c5a727e commit 85deee8

File tree

9 files changed

+934
-173
lines changed

9 files changed

+934
-173
lines changed

core/src/main/java/com/cloud/agent/api/GetExternalConsoleAnswer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public class GetExternalConsoleAnswer extends Answer {
2121

2222
private String host;
2323
private int port;
24+
@LogLevel(LogLevel.Log4jLevel.Off)
2425
private String password;
2526
private String protocol;
2627

framework/extensions/src/test/java/org/apache/cloudstack/framework/extensions/manager/ExtensionsManagerImplTest.java

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@
9292
import org.mockito.InjectMocks;
9393
import org.mockito.Mock;
9494
import org.mockito.MockedStatic;
95-
import org.mockito.Mockito;
9695
import org.mockito.MockitoAnnotations;
9796
import org.mockito.Spy;
9897
import org.mockito.junit.MockitoJUnitRunner;
@@ -102,6 +101,7 @@
102101
import com.cloud.agent.api.Answer;
103102
import com.cloud.agent.api.Command;
104103
import com.cloud.agent.api.RunCustomActionAnswer;
104+
import com.cloud.agent.api.to.VirtualMachineTO;
105105
import com.cloud.alert.AlertManager;
106106
import com.cloud.cluster.ClusterManager;
107107
import com.cloud.cluster.ManagementServerHostVO;
@@ -111,6 +111,7 @@
111111
import com.cloud.exception.AgentUnavailableException;
112112
import com.cloud.exception.InvalidParameterValueException;
113113
import com.cloud.exception.OperationTimedoutException;
114+
import com.cloud.host.Host;
114115
import com.cloud.host.dao.HostDao;
115116
import com.cloud.host.dao.HostDetailsDao;
116117
import com.cloud.hypervisor.ExternalProvisioner;
@@ -1281,7 +1282,7 @@ public void deleteCustomAction_RemoveFails() {
12811282
}
12821283

12831284
private void mockCallerRole(RoleType roleType) {
1284-
CallContext callContextMock = Mockito.mock(CallContext.class);
1285+
CallContext callContextMock = mock(CallContext.class);
12851286
when(CallContext.current()).thenReturn(callContextMock);
12861287
Account accountMock = mock(Account.class);
12871288
when(accountMock.getRoleId()).thenReturn(1L);
@@ -1882,4 +1883,73 @@ public void getExtensionForCluster_WhenNoMappingExists_ReturnsNull() {
18821883
Extension result = extensionsManager.getExtensionForCluster(clusterId);
18831884
assertNull(result);
18841885
}
1886+
1887+
@Test
1888+
public void getInstanceConsole_whenValid() {
1889+
Extension extension = mock(Extension.class);
1890+
when(extension.getType()).thenReturn(Extension.Type.Orchestrator);
1891+
when(extension.getState()).thenReturn(Extension.State.Enabled);
1892+
when(extensionsManager.getExtensionForCluster(anyLong())).thenReturn(extension);
1893+
VirtualMachine vm = mock(VirtualMachine.class);
1894+
Host host = mock(Host.class);
1895+
when(host.getClusterId()).thenReturn(1L);
1896+
Answer expectedAnswer = mock(Answer.class);
1897+
when(virtualMachineManager.toVmTO(any())).thenReturn(mock(VirtualMachineTO.class));
1898+
when(agentMgr.easySend(anyLong(), any())).thenReturn(expectedAnswer);
1899+
Answer result = extensionsManager.getInstanceConsole(vm, host);
1900+
assertNotNull(result);
1901+
assertEquals(expectedAnswer, result);
1902+
}
1903+
1904+
@Test
1905+
public void getInstanceConsole_whenNullExtension() {
1906+
when(extensionsManager.getExtensionForCluster(anyLong())).thenReturn(null);
1907+
VirtualMachine vm = mock(VirtualMachine.class);
1908+
Host host = mock(Host.class);
1909+
when(host.getClusterId()).thenReturn(1L);
1910+
Answer result = extensionsManager.getInstanceConsole(vm, host);
1911+
assertNotNull(result);
1912+
assertFalse(result.getResult());
1913+
}
1914+
1915+
@Test
1916+
public void getInstanceConsole_whenNullExtensionNotOrchestrator() {
1917+
Extension extension = mock(Extension.class);
1918+
when(extensionsManager.getExtensionForCluster(anyLong())).thenReturn(extension);
1919+
VirtualMachine vm = mock(VirtualMachine.class);
1920+
Host host = mock(Host.class);
1921+
when(host.getClusterId()).thenReturn(1L);
1922+
Answer result = extensionsManager.getInstanceConsole(vm, host);
1923+
assertNotNull(result);
1924+
assertFalse(result.getResult());
1925+
}
1926+
1927+
@Test
1928+
public void getInstanceConsole_whenNullExtensionNotEnabled() {
1929+
Extension extension = mock(Extension.class);
1930+
when(extension.getType()).thenReturn(Extension.Type.Orchestrator);
1931+
when(extension.getState()).thenReturn(Extension.State.Disabled);
1932+
when(extensionsManager.getExtensionForCluster(anyLong())).thenReturn(extension);
1933+
VirtualMachine vm = mock(VirtualMachine.class);
1934+
Host host = mock(Host.class);
1935+
when(host.getClusterId()).thenReturn(1L);
1936+
Answer result = extensionsManager.getInstanceConsole(vm, host);
1937+
assertNotNull(result);
1938+
assertFalse(result.getResult());
1939+
}
1940+
1941+
@Test
1942+
public void getInstanceConsole_whenAgentManagerFails() {
1943+
Extension extension = mock(Extension.class);
1944+
when(extension.getType()).thenReturn(Extension.Type.Orchestrator);
1945+
when(extension.getState()).thenReturn(Extension.State.Enabled);
1946+
when(extensionsManager.getExtensionForCluster(anyLong())).thenReturn(extension);
1947+
VirtualMachine vm = mock(VirtualMachine.class);
1948+
Host host = mock(Host.class);
1949+
when(host.getClusterId()).thenReturn(1L);
1950+
when(virtualMachineManager.toVmTO(any())).thenReturn(mock(VirtualMachineTO.class));
1951+
when(agentMgr.easySend(anyLong(), any())).thenReturn(null);
1952+
Answer result = extensionsManager.getInstanceConsole(vm, host);
1953+
assertNull(result);
1954+
}
18851955
}

plugins/hypervisors/external/src/main/java/org/apache/cloudstack/hypervisor/external/provisioner/ExternalPathPayloadProvisioner.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,13 @@ protected VirtualMachineTO getVirtualMachineTO(VirtualMachine vm) {
219219
return hvGuru.implement(profile);
220220
}
221221

222+
protected String getSanitizedJsonStringForLog(String json) {
223+
if (StringUtils.isBlank(json)) {
224+
return json;
225+
}
226+
return json.replaceAll("(\"password\"\\s*:\\s*\")([^\"]*)(\")", "$1****$3");
227+
}
228+
222229
private String getServerProperty(String name) {
223230
Properties props = propertiesRef.get();
224231
if (props == null) {
@@ -485,20 +492,23 @@ public GetExternalConsoleAnswer getInstanceConsole(String hostGuid, String exten
485492
if (!result.first()) {
486493
return new GetExternalConsoleAnswer(cmd, output);
487494
}
488-
logger.debug("Received console details from the external system: {}", output);
495+
logger.debug("Received console details from the external system: {}",
496+
getSanitizedJsonStringForLog(output));
489497
try {
490498
JsonObject jsonObj = JsonParser.parseString(output).getAsJsonObject();
491499
JsonObject consoleObj = jsonObj.has("console") ? jsonObj.getAsJsonObject("console") : null;
492500
if (consoleObj == null) {
493-
logger.error("Missing console object in external console output: {}", output);
501+
logger.error("Missing console object in external console output: {}",
502+
getSanitizedJsonStringForLog(output));
494503
return new GetExternalConsoleAnswer(cmd, "Missing console object in output");
495504
}
496505
String host = consoleObj.has("host") ? consoleObj.get("host").getAsString() : null;
497506
Integer port = consoleObj.has("port") ? Integer.valueOf(consoleObj.get("port").getAsString()) : null;
498507
String password = consoleObj.has("password") ? consoleObj.get("password").getAsString() : null;
499508
String protocol = consoleObj.has("protocol") ? consoleObj.get("protocol").getAsString() : null;
500-
if (ObjectUtils.anyNull(host, port, password)) {
501-
logger.error("Missing required fields in external console output: {}", output);
509+
if (ObjectUtils.anyNull(host, port)) {
510+
logger.error("Missing required fields in external console output: {}",
511+
getSanitizedJsonStringForLog(output));
502512
return new GetExternalConsoleAnswer(cmd, "Missing required fields in output");
503513
}
504514
return new GetExternalConsoleAnswer(cmd, host, port, password, protocol);

plugins/hypervisors/external/src/test/java/org/apache/cloudstack/hypervisor/external/provisioner/ExternalPathPayloadProvisionerTest.java

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
import org.mockito.junit.MockitoJUnitRunner;
6666
import org.springframework.test.util.ReflectionTestUtils;
6767

68+
import com.cloud.agent.api.GetExternalConsoleAnswer;
69+
import com.cloud.agent.api.GetExternalConsoleCommand;
6870
import com.cloud.agent.api.HostVmStateReportEntry;
6971
import com.cloud.agent.api.PrepareExternalProvisioningAnswer;
7072
import com.cloud.agent.api.PrepareExternalProvisioningCommand;
@@ -734,4 +736,215 @@ public void parsePowerStateFromResponseReturnsPowerStateForPlainTextResponse() {
734736
VirtualMachine.PowerState result = provisioner.parsePowerStateFromResponse(vm, response);
735737
assertEquals(VirtualMachine.PowerState.PowerOn, result);
736738
}
739+
740+
@Test
741+
public void getVirtualMachineTOReturnsNullWhenVmIsNull() {
742+
VirtualMachineTO result = provisioner.getVirtualMachineTO(null);
743+
assertNull(result);
744+
}
745+
746+
@Test
747+
public void getVirtualMachineTOReturnsValidTOWhenVmIsNotNull() {
748+
VirtualMachine vm = mock(VirtualMachine.class);
749+
VirtualMachineTO vmTO = mock(VirtualMachineTO.class);
750+
when(hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.External)).thenReturn(hypervisorGuru);
751+
when(hypervisorGuru.implement(any(VirtualMachineProfile.class))).thenReturn(vmTO);
752+
VirtualMachineTO result = provisioner.getVirtualMachineTO(vm);
753+
assertNotNull(result);
754+
assertEquals(vmTO, result);
755+
Mockito.verify(hypervisorGuruManager).getGuru(Hypervisor.HypervisorType.External);
756+
Mockito.verify(hypervisorGuru).implement(any(VirtualMachineProfile.class));
757+
}
758+
759+
@Test
760+
public void getInstanceConsoleReturnsAnswerWhenConsoleDetailsAreValid() {
761+
GetExternalConsoleCommand cmd = mock(GetExternalConsoleCommand.class);
762+
VirtualMachineTO vmTO = mock(VirtualMachineTO.class);
763+
when(cmd.getVirtualMachine()).thenReturn(vmTO);
764+
when(vmTO.getUuid()).thenReturn("test-uuid");
765+
766+
Map<String, Object> accessDetails = new HashMap<>();
767+
when(provisioner.loadAccessDetails(any(), eq(vmTO))).thenReturn(accessDetails);
768+
769+
String validOutput = "{\"console\":{\"host\":\"127.0.0.1\",\"port\":5900,\"password\":\"pass\",\"protocol\":\"vnc\"}}";
770+
doReturn(new Pair<>(true, validOutput)).when(provisioner)
771+
.getInstanceConsoleOnExternalSystem(anyString(), anyString(), anyString(), anyMap(), anyInt());
772+
773+
GetExternalConsoleAnswer result = provisioner.getInstanceConsole("host-guid", "test-extension", "test-extension.sh", cmd);
774+
775+
assertNotNull(result);
776+
assertEquals("127.0.0.1", result.getHost());
777+
assertEquals(5900, result.getPort());
778+
assertEquals("pass", result.getPassword());
779+
assertEquals("vnc", result.getProtocol());
780+
}
781+
782+
@Test
783+
public void getInstanceConsoleReturnsErrorWhenExtensionNotConfigured() {
784+
GetExternalConsoleCommand cmd = mock(GetExternalConsoleCommand.class);
785+
when(provisioner.getExtensionCheckedPath(anyString(), anyString())).thenReturn(null);
786+
787+
GetExternalConsoleAnswer result = provisioner.getInstanceConsole("host-guid",
788+
"test-extension", "test-extension.sh", cmd);
789+
790+
assertNotNull(result);
791+
assertEquals("Extension not configured", result.getDetails());
792+
}
793+
794+
@Test
795+
public void getInstanceConsoleReturnsErrorWhenExternalSystemFails() {
796+
GetExternalConsoleCommand cmd = mock(GetExternalConsoleCommand.class);
797+
VirtualMachineTO vmTO = mock(VirtualMachineTO.class);
798+
when(cmd.getVirtualMachine()).thenReturn(vmTO);
799+
when(vmTO.getUuid()).thenReturn("test-uuid");
800+
801+
doReturn(new Pair<>(false, "External system error")).when(provisioner)
802+
.getInstanceConsoleOnExternalSystem(anyString(), anyString(), anyString(), anyMap(), anyInt());
803+
804+
GetExternalConsoleAnswer result = provisioner.getInstanceConsole("host-guid", "test-extension", "test-extension.sh", cmd);
805+
806+
assertNotNull(result);
807+
assertEquals("External system error", result.getDetails());
808+
}
809+
810+
@Test
811+
public void getInstanceConsoleReturnsErrorWhenConsoleObjectIsMissing() {
812+
GetExternalConsoleCommand cmd = mock(GetExternalConsoleCommand.class);
813+
VirtualMachineTO vmTO = mock(VirtualMachineTO.class);
814+
when(cmd.getVirtualMachine()).thenReturn(vmTO);
815+
when(vmTO.getUuid()).thenReturn("test-uuid");
816+
817+
String invalidOutput = "{\"invalid_key\":\"value\"}";
818+
doReturn(new Pair<>(true, invalidOutput)).when(provisioner)
819+
.getInstanceConsoleOnExternalSystem(anyString(), anyString(), anyString(), anyMap(), anyInt());
820+
821+
GetExternalConsoleAnswer result = provisioner.getInstanceConsole("host-guid", "test-extension", "test-extension.sh", cmd);
822+
823+
assertNotNull(result);
824+
assertEquals("Missing console object in output", result.getDetails());
825+
}
826+
827+
@Test
828+
public void getInstanceConsoleReturnsErrorWhenRequiredFieldsAreMissing() {
829+
GetExternalConsoleCommand cmd = mock(GetExternalConsoleCommand.class);
830+
VirtualMachineTO vmTO = mock(VirtualMachineTO.class);
831+
when(cmd.getVirtualMachine()).thenReturn(vmTO);
832+
when(vmTO.getUuid()).thenReturn("test-uuid");
833+
834+
String incompleteOutput = "{\"console\":{\"host\":\"127.0.0.1\"}}";
835+
doReturn(new Pair<>(true, incompleteOutput)).when(provisioner)
836+
.getInstanceConsoleOnExternalSystem(anyString(), anyString(), anyString(), anyMap(), anyInt());
837+
838+
GetExternalConsoleAnswer result = provisioner.getInstanceConsole("host-guid", "test-extension", "test-extension.sh", cmd);
839+
840+
assertNotNull(result);
841+
assertEquals("Missing required fields in output", result.getDetails());
842+
}
843+
844+
@Test
845+
public void getInstanceConsoleReturnsErrorWhenOutputParsingFails() {
846+
GetExternalConsoleCommand cmd = mock(GetExternalConsoleCommand.class);
847+
VirtualMachineTO vmTO = mock(VirtualMachineTO.class);
848+
when(cmd.getVirtualMachine()).thenReturn(vmTO);
849+
when(vmTO.getUuid()).thenReturn("test-uuid");
850+
851+
String malformedOutput = "{console:invalid}";
852+
doReturn(new Pair<>(true, malformedOutput)).when(provisioner)
853+
.getInstanceConsoleOnExternalSystem(anyString(), anyString(), anyString(), anyMap(), anyInt());
854+
855+
GetExternalConsoleAnswer result = provisioner.getInstanceConsole("host-guid", "test-extension", "test-extension.sh", cmd);
856+
857+
assertNotNull(result);
858+
assertEquals("Failed to parse output", result.getDetails());
859+
}
860+
861+
@Test
862+
public void getInstanceConsoleOnExternalSystemReturnsSuccessWhenCommandExecutesSuccessfully() {
863+
String extensionName = "test-extension";
864+
String filename = "test-script.sh";
865+
String vmUUID = "test-vm-uuid";
866+
Map<String, Object> accessDetails = new HashMap<>();
867+
int wait = 30;
868+
869+
doReturn(new Pair<>(true, "Console details")).when(provisioner)
870+
.executeExternalCommand(eq(extensionName), eq("getconsole"), eq(accessDetails), eq(wait), anyString(), eq(filename));
871+
872+
Pair<Boolean, String> result = provisioner.getInstanceConsoleOnExternalSystem(extensionName, filename, vmUUID, accessDetails, wait);
873+
874+
assertTrue(result.first());
875+
assertEquals("Console details", result.second());
876+
}
877+
878+
@Test
879+
public void getInstanceConsoleOnExternalSystemReturnsFailureWhenCommandFails() {
880+
String extensionName = "test-extension";
881+
String filename = "test-script.sh";
882+
String vmUUID = "test-vm-uuid";
883+
Map<String, Object> accessDetails = new HashMap<>();
884+
int wait = 30;
885+
886+
doReturn(new Pair<>(false, "Failed to get console")).when(provisioner)
887+
.executeExternalCommand(eq(extensionName), eq("getconsole"), eq(accessDetails), eq(wait), anyString(), eq(filename));
888+
889+
Pair<Boolean, String> result = provisioner.getInstanceConsoleOnExternalSystem(extensionName, filename, vmUUID, accessDetails, wait);
890+
891+
assertFalse(result.first());
892+
assertEquals("Failed to get console", result.second());
893+
}
894+
895+
@Test
896+
public void getInstanceConsoleOnExternalSystemHandlesNullResponseGracefully() {
897+
String extensionName = "test-extension";
898+
String filename = "test-script.sh";
899+
String vmUUID = "test-vm-uuid";
900+
Map<String, Object> accessDetails = new HashMap<>();
901+
int wait = 30;
902+
903+
doReturn(null).when(provisioner)
904+
.executeExternalCommand(eq(extensionName), eq("getconsole"), eq(accessDetails), eq(wait), anyString(), eq(filename));
905+
906+
Pair<Boolean, String> result = provisioner.getInstanceConsoleOnExternalSystem(extensionName, filename, vmUUID, accessDetails, wait);
907+
908+
assertNull(result);
909+
}
910+
911+
@Test
912+
public void getSanitizedJsonStringForLogReturnsNullWhenInputIsNull() {
913+
String result = provisioner.getSanitizedJsonStringForLog(null);
914+
assertNull(result);
915+
}
916+
917+
@Test
918+
public void getSanitizedJsonStringForLogReturnsEmptyWhenInputIsEmpty() {
919+
String result = provisioner.getSanitizedJsonStringForLog("");
920+
assertEquals("", result);
921+
}
922+
923+
@Test
924+
public void getSanitizedJsonStringForLogReturnsSameStringWhenNoPasswordField() {
925+
String json = "{\"key\":\"value\"}";
926+
String result = provisioner.getSanitizedJsonStringForLog(json);
927+
assertEquals(json, result);
928+
}
929+
930+
@Test
931+
public void getSanitizedJsonStringForLogMasksPasswordField() {
932+
String json = "{\"password\":\"secret\"}";
933+
String result = provisioner.getSanitizedJsonStringForLog(json);
934+
assertEquals("{\"password\":\"****\"}", result);
935+
}
936+
937+
@Test
938+
public void getSanitizedJsonStringForLogHandlesMultiplePasswordFields() {
939+
String json = "{\"password\":\"secret\",\"nested\":{\"password\":\"anotherSecret\"}}";
940+
String result = provisioner.getSanitizedJsonStringForLog(json);
941+
assertEquals("{\"password\":\"****\",\"nested\":{\"password\":\"****\"}}", result);
942+
}
943+
944+
@Test
945+
public void getSanitizedJsonStringForLogHandlesMalformedJsonGracefully() {
946+
String json = "{password:\"secret\"";
947+
String result = provisioner.getSanitizedJsonStringForLog(json);
948+
assertEquals("{password:\"secret\"", result);
949+
}
737950
}

server/src/main/java/com/cloud/server/ManagementServer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,6 @@ public interface ManagementServer extends ManagementService, PluggableService {
7676

7777
Pair<Boolean, String> updateSystemVM(VMInstanceVO systemVM, boolean forced);
7878

79-
Answer getExternalConsole(VirtualMachine vm, Host host);
79+
Answer getExternalVmConsole(VirtualMachine vm, Host host);
8080

8181
}

server/src/main/java/com/cloud/server/ManagementServerImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5813,7 +5813,7 @@ public boolean removeManagementServer(RemoveManagementServerCmd cmd) {
58135813
}
58145814

58155815
@Override
5816-
public Answer getExternalConsole(VirtualMachine vm, Host host) {
5816+
public Answer getExternalVmConsole(VirtualMachine vm, Host host) {
58175817
return extensionsManager.getInstanceConsole(vm, host);
58185818
}
58195819
}

0 commit comments

Comments
 (0)