Skip to content

Commit 39d07a0

Browse files
committed
libvirt start nbd server UT
1 parent d3798e1 commit 39d07a0

2 files changed

Lines changed: 245 additions & 2 deletions

File tree

plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartBackupCommandWrapperTest.java

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ public void setUp() {
4949
@Test
5050
public void testExecuteStoppedVmBitmapAddSuccess() {
5151
Mockito.when(command.isStoppedVM()).thenReturn(true);
52-
Mockito.when(command.getVmName()).thenReturn("i-2-3-VM");
5352
Mockito.when(command.getToCheckpointId()).thenReturn("cp-stopped-1");
5453
Mockito.when(command.getDiskPathUuidMap()).thenReturn(Map.of("/path/disk1.qcow2", "vol-1"));
5554

@@ -66,7 +65,6 @@ public void testExecuteStoppedVmBitmapAddSuccess() {
6665
@Test
6766
public void testExecuteStoppedVmBitmapAddFailure() {
6867
Mockito.when(command.isStoppedVM()).thenReturn(true);
69-
Mockito.when(command.getVmName()).thenReturn("i-2-3-VM");
7068
Mockito.when(command.getToCheckpointId()).thenReturn("cp-stopped-2");
7169
Mockito.when(command.getDiskPathUuidMap()).thenReturn(Map.of("/path/disk1.qcow2", "vol-1"));
7270

@@ -115,4 +113,70 @@ public void testExecuteRunningVmBackupBeginSuccess() {
115113
Assert.assertTrue(answer.getDetails().contains("Backup started successfully"));
116114
}
117115
}
116+
117+
@Test
118+
public void testExecuteRunningVmCheckpointRedefineFailure() {
119+
Mockito.when(command.isStoppedVM()).thenReturn(false);
120+
Mockito.when(command.getVmName()).thenReturn("i-2-3-VM");
121+
Mockito.when(command.getToCheckpointId()).thenReturn("cp-running-3");
122+
Mockito.when(command.getFromCheckpointId()).thenReturn("cp-running-missing");
123+
Mockito.when(command.getFromCheckpointCreateTime()).thenReturn(12345L);
124+
Mockito.when(command.getSocket()).thenReturn("sock-3");
125+
126+
try (MockedConstruction<Script> ignored = Mockito.mockConstruction(Script.class, (mock, context) -> {
127+
int constructionIndex = context.getCount();
128+
if (constructionIndex == 1) {
129+
Mockito.when(mock.execute()).thenReturn("checkpoint missing");
130+
} else if (constructionIndex == 2) {
131+
Mockito.when(mock.execute()).thenReturn("checkpoint redefine failed");
132+
} else {
133+
Mockito.when(mock.execute()).thenReturn(null);
134+
}
135+
})) {
136+
Answer answer = wrapper.execute(command, resource);
137+
138+
Assert.assertFalse(answer.getResult());
139+
Assert.assertTrue(answer.getDetails().contains("Failed to redefine from-checkpoint cp-running-missing"));
140+
}
141+
}
142+
143+
@Test
144+
public void testExecuteRunningVmBackupBeginFailure() {
145+
Mockito.when(command.isStoppedVM()).thenReturn(false);
146+
Mockito.when(command.getVmName()).thenReturn("i-2-3-VM");
147+
Mockito.when(command.getToCheckpointId()).thenReturn("cp-running-4");
148+
Mockito.when(command.getFromCheckpointId()).thenReturn(null);
149+
Mockito.when(command.getSocket()).thenReturn("sock-4");
150+
Mockito.when(command.getDiskPathUuidMap()).thenReturn(Map.of("/path/disk1.qcow2", "vol-1"));
151+
Mockito.when(resource.getDiskPathLabelMap("i-2-3-VM")).thenReturn(Map.of("/path/disk1.qcow2", "vda"));
152+
153+
try (MockedConstruction<Script> ignored = Mockito.mockConstruction(Script.class, (mock, context) -> {
154+
if (context.getCount() == 1) {
155+
Mockito.when(mock.execute()).thenReturn("backup begin failed");
156+
} else {
157+
Mockito.when(mock.execute()).thenReturn(null);
158+
}
159+
})) {
160+
Answer answer = wrapper.execute(command, resource);
161+
162+
Assert.assertFalse(answer.getResult());
163+
Assert.assertTrue(answer.getDetails().contains("Backup begin failed: backup begin failed"));
164+
}
165+
}
166+
167+
@Test
168+
public void testExecuteRunningVmCreateBackupXmlExceptionReturnsFailure() {
169+
Mockito.when(command.isStoppedVM()).thenReturn(false);
170+
Mockito.when(command.getVmName()).thenReturn("i-2-3-VM");
171+
Mockito.when(command.getToCheckpointId()).thenReturn("cp-running-5");
172+
Mockito.when(command.getFromCheckpointId()).thenReturn(null);
173+
Mockito.when(command.getSocket()).thenReturn("sock-5");
174+
Mockito.when(command.getDiskPathUuidMap()).thenReturn(Map.of("/path/disk1.qcow2", "vol-1"));
175+
Mockito.when(resource.getDiskPathLabelMap("i-2-3-VM")).thenThrow(new RuntimeException("disk labels unavailable"));
176+
177+
Answer answer = wrapper.execute(command, resource);
178+
179+
Assert.assertFalse(answer.getResult());
180+
Assert.assertTrue(answer.getDetails().contains("Error starting backup: disk labels unavailable"));
181+
}
118182
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
package com.cloud.hypervisor.kvm.resource.wrapper;
18+
19+
import org.apache.cloudstack.backup.StartNBDServerAnswer;
20+
import org.apache.cloudstack.backup.StartNBDServerCommand;
21+
import org.junit.Assert;
22+
import org.junit.Before;
23+
import org.junit.Test;
24+
import org.junit.runner.RunWith;
25+
import org.mockito.MockedConstruction;
26+
import org.mockito.MockedStatic;
27+
import org.mockito.Mockito;
28+
import org.mockito.junit.MockitoJUnitRunner;
29+
30+
import com.cloud.agent.api.Answer;
31+
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
32+
import com.cloud.utils.script.Script;
33+
34+
@RunWith(MockitoJUnitRunner.class)
35+
public class LibvirtStartNBDServerCommandWrapperTest {
36+
37+
private LibvirtStartNBDServerCommandWrapper wrapper;
38+
private StartNBDServerCommand command;
39+
private LibvirtComputingResource resource;
40+
41+
@Before
42+
public void setUp() {
43+
wrapper = new LibvirtStartNBDServerCommandWrapper();
44+
command = Mockito.mock(StartNBDServerCommand.class);
45+
resource = Mockito.mock(LibvirtComputingResource.class);
46+
Mockito.when(command.getVolumePath()).thenReturn("/path/disk.qcow2");
47+
Mockito.when(command.getExportName()).thenReturn("vol-1");
48+
Mockito.when(command.getSocket()).thenReturn("sock-1");
49+
Mockito.when(command.getTransferId()).thenReturn("transfer-1");
50+
Mockito.when(command.getDirection()).thenReturn("upload");
51+
Mockito.when(command.getFromCheckpointId()).thenReturn(null);
52+
}
53+
54+
@Test
55+
public void testExecuteMissingVolumePathReturnsFailure() {
56+
Mockito.when(command.getVolumePath()).thenReturn(null);
57+
58+
Answer answer = wrapper.execute(command, resource);
59+
60+
Assert.assertFalse(answer.getResult());
61+
Assert.assertEquals("Volume path is required for the nbd server", answer.getDetails());
62+
}
63+
64+
@Test
65+
public void testExecuteMissingExportNameReturnsFailure() {
66+
Mockito.when(command.getExportName()).thenReturn(" ");
67+
68+
Answer answer = wrapper.execute(command, resource);
69+
70+
Assert.assertFalse(answer.getResult());
71+
Assert.assertEquals("Export name is required for the nbd server", answer.getDetails());
72+
}
73+
74+
@Test
75+
public void testExecuteMissingSocketReturnsFailure() {
76+
Mockito.when(command.getSocket()).thenReturn("");
77+
78+
Answer answer = wrapper.execute(command, resource);
79+
80+
Assert.assertFalse(answer.getResult());
81+
Assert.assertEquals("Socket is required for the nbd server", answer.getDetails());
82+
}
83+
84+
@Test
85+
public void testExecuteAlreadyActiveServiceReturnsFailure() {
86+
try (MockedConstruction<Script> ignored = Mockito.mockConstruction(Script.class, (mock, context) ->
87+
Mockito.when(mock.execute()).thenReturn(null))) {
88+
Answer answer = wrapper.execute(command, resource);
89+
90+
Assert.assertFalse(answer.getResult());
91+
Assert.assertTrue(answer.getDetails().contains("already running"));
92+
}
93+
}
94+
95+
@Test
96+
public void testExecuteStartScriptFailureReturnsFailure() {
97+
try (MockedConstruction<Script> ignored = Mockito.mockConstruction(Script.class, (mock, context) -> {
98+
if (context.getCount() == 1) {
99+
Mockito.when(mock.execute()).thenReturn("inactive");
100+
} else if (context.getCount() == 2) {
101+
Mockito.when(mock.execute()).thenReturn("start failed");
102+
} else {
103+
Mockito.when(mock.execute()).thenReturn(null);
104+
}
105+
})) {
106+
Answer answer = wrapper.execute(command, resource);
107+
108+
Assert.assertFalse(answer.getResult());
109+
Assert.assertTrue(answer.getDetails().contains("Failed to start qemu-nbd service: start failed"));
110+
}
111+
}
112+
113+
@Test
114+
public void testExecuteInterruptedWhileWaitingReturnsFailure() {
115+
try (MockedConstruction<Script> ignored = Mockito.mockConstruction(Script.class, (mock, context) -> {
116+
if (context.getCount() <= 2) {
117+
Mockito.when(mock.execute()).thenReturn(context.getCount() == 1 ? "inactive" : null);
118+
} else {
119+
Mockito.when(mock.execute()).thenReturn("inactive");
120+
}
121+
})) {
122+
Thread.currentThread().interrupt();
123+
Answer answer = wrapper.execute(command, resource);
124+
125+
Assert.assertFalse(answer.getResult());
126+
Assert.assertTrue(answer.getDetails().contains("Interrupted while waiting"));
127+
Assert.assertTrue(Thread.currentThread().isInterrupted());
128+
Thread.interrupted();
129+
}
130+
}
131+
132+
@Test
133+
public void testExecuteSuccessReturnsTransferDetails() {
134+
try (MockedConstruction<Script> ignored = Mockito.mockConstruction(Script.class, (mock, context) -> {
135+
if (context.getCount() == 1) {
136+
Mockito.when(mock.execute()).thenReturn("inactive");
137+
} else {
138+
Mockito.when(mock.execute()).thenReturn(null);
139+
}
140+
})) {
141+
Answer answer = wrapper.execute(command, resource);
142+
143+
Assert.assertTrue(answer.getResult());
144+
Assert.assertTrue(answer instanceof StartNBDServerAnswer);
145+
StartNBDServerAnswer startNBDServerAnswer = (StartNBDServerAnswer) answer;
146+
Assert.assertEquals("transfer-1", startNBDServerAnswer.getImageTransferId());
147+
Assert.assertEquals("nbd+unix:///sock-1", startNBDServerAnswer.getTransferUrl());
148+
}
149+
}
150+
151+
@Test
152+
public void testExecuteAddsBitmapOptionWhenBitmapExists() {
153+
Mockito.when(command.getFromCheckpointId()).thenReturn("cp-1");
154+
155+
try (MockedStatic<Script> scriptStaticMock = Mockito.mockStatic(Script.class);
156+
MockedConstruction<Script> scriptConstruction = Mockito.mockConstruction(Script.class, (mock, context) -> {
157+
if (context.getCount() == 1) {
158+
Mockito.when(mock.execute()).thenReturn("inactive");
159+
} else {
160+
Mockito.when(mock.execute()).thenReturn(null);
161+
}
162+
})) {
163+
scriptStaticMock.when(() -> Script.runBashScriptIgnoreExitValue(
164+
Mockito.anyString(), Mockito.anyInt())).thenReturn(
165+
"{\"format-specific\":{\"data\":{\"bitmaps\":[{\"name\":\"cp-1\"}]}}}");
166+
167+
Answer answer = wrapper.execute(command, resource);
168+
169+
Assert.assertTrue(answer.getResult());
170+
Script startScript = scriptConstruction.constructed().get(1);
171+
boolean bitmapOptionAdded = Mockito.mockingDetails(startScript).getInvocations().stream().anyMatch(invocation ->
172+
invocation.getMethod().getName().equals("add")
173+
&& invocation.getArguments().length > 0
174+
&& invocation.getArguments()[0] instanceof String
175+
&& ((String) invocation.getArguments()[0]).contains("-B cp-1"));
176+
Assert.assertTrue(bitmapOptionAdded);
177+
}
178+
}
179+
}

0 commit comments

Comments
 (0)