Skip to content

Commit 06e55c4

Browse files
committed
CLOUDSTACK-9782: Nested-oobm CloudStack plugin
Nested out-of-band management plugin to work with hosts that are VMs in a CloudStack env. Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
1 parent 33f2ee2 commit 06e55c4

16 files changed

Lines changed: 594 additions & 11 deletions

File tree

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ env:
6464
smoke/test_non_contigiousvlan"
6565

6666
- TESTS="smoke/test_outofbandmanagement
67+
smoke/test_outofbandmanagement_nestedplugin
6768
smoke/test_over_provisioning
6869
smoke/test_password_server
6970
smoke/test_portable_publicip

api/src/org/apache/cloudstack/api/response/OutOfBandManagementResponse.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public OutOfBandManagementResponse(final OutOfBandManagement outOfBandManagement
9191
this.setDriver(outOfBandManagementConfig.getDriver());
9292
this.setIpAddress(outOfBandManagementConfig.getAddress());
9393
if (outOfBandManagementConfig.getPort() != null) {
94-
this.setPort(String.valueOf(outOfBandManagementConfig.getPort()));
94+
this.setPort(outOfBandManagementConfig.getPort());
9595
}
9696
this.setUsername(outOfBandManagementConfig.getUsername());
9797
if (!Strings.isNullOrEmpty(outOfBandManagementConfig.getPassword())) {

api/src/org/apache/cloudstack/outofbandmanagement/OutOfBandManagement.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public interface OutOfBandManagement extends StateObject<OutOfBandManagement.Pow
3939

4040
String getAddress();
4141

42-
Integer getPort();
42+
String getPort();
4343

4444
String getUsername();
4545

@@ -53,7 +53,7 @@ public interface OutOfBandManagement extends StateObject<OutOfBandManagement.Pow
5353

5454
void setAddress(String address);
5555

56-
void setPort(Integer port);
56+
void setPort(String port);
5757

5858
void setUsername(String username);
5959

client/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,11 @@
264264
<artifactId>cloud-plugin-outofbandmanagement-driver-ipmitool</artifactId>
265265
<version>${project.version}</version>
266266
</dependency>
267+
<dependency>
268+
<groupId>org.apache.cloudstack</groupId>
269+
<artifactId>cloud-plugin-outofbandmanagement-driver-nested-cloudstack</artifactId>
270+
<version>${project.version}</version>
271+
</dependency>
267272
<dependency>
268273
<groupId>org.apache.cloudstack</groupId>
269274
<artifactId>cloud-mom-rabbitmq</artifactId>

engine/schema/src/org/apache/cloudstack/outofbandmanagement/OutOfBandManagementVO.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public class OutOfBandManagementVO implements OutOfBandManagement {
5959
private String address;
6060

6161
@Column(name = "port")
62-
private Integer port;
62+
private String port;
6363

6464
@Column(name = "username")
6565
private String username;
@@ -121,7 +121,7 @@ public String getAddress() {
121121
}
122122

123123
@Override
124-
public Integer getPort() {
124+
public String getPort() {
125125
return port;
126126
}
127127

@@ -173,7 +173,7 @@ public void setAddress(String address) {
173173
}
174174

175175
@Override
176-
public void setPort(Integer port) {
176+
public void setPort(String port) {
177177
this.port = port;
178178
}
179179

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
-->
19+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
20+
<modelVersion>4.0.0</modelVersion>
21+
<artifactId>cloud-plugin-outofbandmanagement-driver-nested-cloudstack</artifactId>
22+
<name>Apache CloudStack Plugin - Power Management Driver nested-cloudstack</name>
23+
<parent>
24+
<groupId>org.apache.cloudstack</groupId>
25+
<artifactId>cloudstack-plugins</artifactId>
26+
<version>4.11.0.0-SNAPSHOT</version>
27+
<relativePath>../../pom.xml</relativePath>
28+
</parent>
29+
<dependencies>
30+
<dependency>
31+
<groupId>org.apache.cloudstack</groupId>
32+
<artifactId>cloud-utils</artifactId>
33+
<version>${project.version}</version>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.apache.cloudstack</groupId>
37+
<artifactId>cloud-api</artifactId>
38+
<version>${project.version}</version>
39+
</dependency>
40+
<dependency>
41+
<groupId>br.com.autonomiccs</groupId>
42+
<artifactId>apache-cloudstack-java-client</artifactId>
43+
<version>1.0.4</version>
44+
</dependency>
45+
</dependencies>
46+
</project>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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+
name=nested-cloudstack
18+
parent=outofbandmanagement
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
-->
19+
<beans xmlns="http://www.springframework.org/schema/beans"
20+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
21+
xsi:schemaLocation="http://www.springframework.org/schema/beans
22+
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
23+
>
24+
25+
<bean id="nestedCloudStackOutOfBandManagementDriver" class="org.apache.cloudstack.outofbandmanagement.driver.nestedcloudstack.NestedCloudStackOutOfBandManagementDriver">
26+
<property name="name" value="NESTEDCLOUDSTACK" />
27+
</bean>
28+
29+
</beans>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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 org.apache.cloudstack.outofbandmanagement.driver.nestedcloudstack;
18+
19+
import br.com.autonomiccs.apacheCloudStack.client.ApacheCloudStackClient;
20+
import br.com.autonomiccs.apacheCloudStack.client.ApacheCloudStackRequest;
21+
import br.com.autonomiccs.apacheCloudStack.client.beans.ApacheCloudStackUser;
22+
import br.com.autonomiccs.apacheCloudStack.exceptions.ApacheCloudStackClientRequestRuntimeException;
23+
import com.cloud.utils.component.AdapterBase;
24+
import com.cloud.utils.exception.CloudRuntimeException;
25+
import com.fasterxml.jackson.databind.ObjectMapper;
26+
import com.google.common.base.Strings;
27+
import com.google.common.collect.ImmutableMap;
28+
import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement;
29+
import org.apache.cloudstack.outofbandmanagement.OutOfBandManagementDriver;
30+
import org.apache.cloudstack.outofbandmanagement.driver.OutOfBandManagementDriverChangePasswordCommand;
31+
import org.apache.cloudstack.outofbandmanagement.driver.OutOfBandManagementDriverCommand;
32+
import org.apache.cloudstack.outofbandmanagement.driver.OutOfBandManagementDriverPowerCommand;
33+
import org.apache.cloudstack.outofbandmanagement.driver.OutOfBandManagementDriverResponse;
34+
import org.apache.log4j.Logger;
35+
36+
import java.io.IOException;
37+
import java.util.List;
38+
import java.util.Map;
39+
40+
public final class NestedCloudStackOutOfBandManagementDriver extends AdapterBase implements OutOfBandManagementDriver {
41+
private static final Logger LOG = Logger.getLogger(NestedCloudStackOutOfBandManagementDriver.class);
42+
43+
public OutOfBandManagementDriverResponse execute(final OutOfBandManagementDriverCommand cmd) {
44+
OutOfBandManagementDriverResponse response = new OutOfBandManagementDriverResponse(null, "Unsupported Command", false);
45+
46+
if (cmd instanceof OutOfBandManagementDriverPowerCommand) {
47+
response = execute((OutOfBandManagementDriverPowerCommand) cmd);
48+
} else if (cmd instanceof OutOfBandManagementDriverChangePasswordCommand) {
49+
throw new CloudRuntimeException("Change password operation is not supported by the nested-cloudstack out-of-band management driver");
50+
}
51+
52+
return response;
53+
}
54+
55+
protected void ensureOptionExists(final ImmutableMap<OutOfBandManagement.Option, String> options, final OutOfBandManagement.Option option) {
56+
if (options != null && option != null && options.containsKey(option) && !Strings.isNullOrEmpty(options.get(option))) {
57+
return;
58+
}
59+
throw new CloudRuntimeException("Invalid out-of-band management configuration detected for the nested-cloudstack driver");
60+
}
61+
62+
protected OutOfBandManagement.PowerState getNestedVMPowerState(final String jsonResponse) {
63+
if (Strings.isNullOrEmpty(jsonResponse)) {
64+
return OutOfBandManagement.PowerState.Unknown;
65+
}
66+
67+
final ObjectMapper mapper = new ObjectMapper();
68+
try {
69+
Map<String, Object> listResponse = mapper.readValue(jsonResponse, Map.class);
70+
if (listResponse != null && listResponse.containsKey("listvirtualmachinesresponse")
71+
&& ((Map<String, Object>) listResponse.get("listvirtualmachinesresponse")).containsKey("virtualmachine")) {
72+
Map<String, String> vmResponse = ((Map<String, List<Map<String, String>>>) listResponse.get("listvirtualmachinesresponse")).get("virtualmachine").get(0);
73+
if (vmResponse != null && vmResponse.containsKey("state")) {
74+
if("Running".equals(vmResponse.get("state"))) {
75+
return OutOfBandManagement.PowerState.On;
76+
} else if("Stopped".equals(vmResponse.get("state"))) {
77+
return OutOfBandManagement.PowerState.Off;
78+
}
79+
}
80+
}
81+
} catch (IOException e) {
82+
LOG.warn("Exception caught while de-serializing and reading state of the nested-cloudstack VM from the response: " + jsonResponse + ", with exception:", e);
83+
}
84+
return OutOfBandManagement.PowerState.Unknown;
85+
}
86+
87+
private OutOfBandManagementDriverResponse execute(final OutOfBandManagementDriverPowerCommand cmd) {
88+
if (cmd == null || cmd.getPowerOperation() == null) {
89+
throw new CloudRuntimeException("Invalid out-of-band management power command provided to the nested-cloudstack driver");
90+
}
91+
92+
final ImmutableMap<OutOfBandManagement.Option, String> options = cmd.getOptions();
93+
ensureOptionExists(options, OutOfBandManagement.Option.ADDRESS);
94+
ensureOptionExists(options, OutOfBandManagement.Option.PORT);
95+
ensureOptionExists(options, OutOfBandManagement.Option.USERNAME);
96+
ensureOptionExists(options, OutOfBandManagement.Option.PASSWORD);
97+
98+
final String url = options.get(OutOfBandManagement.Option.ADDRESS);
99+
final String vmUuid = options.get(OutOfBandManagement.Option.PORT);
100+
final String apiKey = options.get(OutOfBandManagement.Option.USERNAME);
101+
final String secretKey = options.get(OutOfBandManagement.Option.PASSWORD);
102+
103+
final ApacheCloudStackUser apacheCloudStackUser = new ApacheCloudStackUser(secretKey, apiKey);
104+
final ApacheCloudStackClient client = new ApacheCloudStackClient(url, apacheCloudStackUser);
105+
client.setValidateServerHttpsCertificate(false);
106+
client.setShouldRequestsExpire(false);
107+
client.setConnectionTimeout((int) cmd.getTimeout().getStandardSeconds());
108+
109+
String apiName = "listVirtualMachines";
110+
switch (cmd.getPowerOperation()) {
111+
case ON:
112+
apiName = "startVirtualMachine";
113+
break;
114+
case OFF:
115+
case SOFT:
116+
apiName = "stopVirtualMachine";
117+
break;
118+
case CYCLE:
119+
case RESET:
120+
apiName = "rebootVirtualMachine";
121+
break;
122+
}
123+
124+
final ApacheCloudStackRequest apacheCloudStackRequest = new ApacheCloudStackRequest(apiName);
125+
apacheCloudStackRequest.addParameter("response", "json");
126+
apacheCloudStackRequest.addParameter("forced", "true");
127+
apacheCloudStackRequest.addParameter("id", vmUuid);
128+
129+
final String apiResponse;
130+
try {
131+
apiResponse = client.executeRequest(apacheCloudStackRequest);
132+
} catch (final ApacheCloudStackClientRequestRuntimeException e) {
133+
LOG.error("Nested CloudStack oobm plugin failed due to API error: ", e);
134+
final OutOfBandManagementDriverResponse failedResponse = new OutOfBandManagementDriverResponse(e.getResponse(), "HTTP error code: " + e.getStatusCode(), false);
135+
if (e.getStatusCode() == 401) {
136+
failedResponse.setAuthFailure(true);
137+
}
138+
return failedResponse;
139+
}
140+
141+
final OutOfBandManagementDriverResponse response = new OutOfBandManagementDriverResponse(apiResponse, null, true);
142+
if (OutOfBandManagement.PowerOperation.STATUS.equals(cmd.getPowerOperation())) {
143+
response.setPowerState(getNestedVMPowerState(apiResponse));
144+
}
145+
return response;
146+
}
147+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package org.apache.cloudstack.outofbandmanagement.driver.nestedcloudstack;
21+
22+
import com.cloud.utils.exception.CloudRuntimeException;
23+
import com.google.common.collect.ImmutableMap;
24+
import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement;
25+
import org.junit.Assert;
26+
import org.junit.Test;
27+
import org.junit.runner.RunWith;
28+
import org.mockito.runners.MockitoJUnitRunner;
29+
30+
import java.io.IOException;
31+
32+
@RunWith(MockitoJUnitRunner.class)
33+
public class NestedCloudStackOutOfBandManagementDriverTest {
34+
private NestedCloudStackOutOfBandManagementDriver driver = new NestedCloudStackOutOfBandManagementDriver();
35+
36+
@Test
37+
public void testEnsureOptionExists() throws IOException {
38+
final ImmutableMap.Builder<OutOfBandManagement.Option, String> builder = ImmutableMap.builder();
39+
builder.put(OutOfBandManagement.Option.ADDRESS, "http://some.cloud/client/api");
40+
final ImmutableMap<OutOfBandManagement.Option, String> options = builder.build();
41+
driver.ensureOptionExists(options, OutOfBandManagement.Option.ADDRESS);
42+
43+
boolean caughtException = false;
44+
try {
45+
driver.ensureOptionExists(options, OutOfBandManagement.Option.PORT);
46+
} catch (CloudRuntimeException e) {
47+
caughtException = true;
48+
}
49+
Assert.assertTrue(caughtException);
50+
}
51+
52+
@Test
53+
public void testIsVMRunningTrue() throws IOException {
54+
String json = "{\"listvirtualmachinesresponse\":{\"count\":1,\"virtualmachine\":[{\"id\":\"38fa7380-9543-486a-b083-190ecf726ba4\",\"name\":\"test-vm\",\"displayname\":\"test-vm\",\"account\":\"admin\",\"userid\":\"78ed9ce8-f3ee-11e4-91ab-00012e4fde1c\",\"username\":\"admin\",\"domainid\":\"53601d4b-f3ee-11e4-91ab-00012e4fde1c\",\"domain\":\"ROOT\",\"created\":\"2017-04-04T19:50:56+0200\",\"state\":\"Running\"}]}}";
55+
Assert.assertEquals(driver.getNestedVMPowerState(json), OutOfBandManagement.PowerState.On);
56+
}
57+
58+
@Test
59+
public void testIsVMRunningFalse() throws IOException {
60+
String json = "{\"listvirtualmachinesresponse\":{\"count\":1,\"virtualmachine\":[{\"id\":\"38fa7380-9543-486a-b083-190ecf726ba4\",\"name\":\"test-vm\",\"displayname\":\"test-vm\",\"account\":\"admin\",\"userid\":\"78ed9ce8-f3ee-11e4-91ab-00012e4fde1c\",\"username\":\"admin\",\"domainid\":\"53601d4b-f3ee-11e4-91ab-00012e4fde1c\",\"domain\":\"ROOT\",\"created\":\"2017-04-04T19:50:56+0200\",\"state\":\"Stopped\"}]}}";
61+
Assert.assertEquals(driver.getNestedVMPowerState(json), OutOfBandManagement.PowerState.Off);
62+
}
63+
64+
@Test
65+
public void testIsVMRunningInvalidJson() throws IOException {
66+
String json = "{\"listvirtualmachinesresponse\":{\"count\":1,\"virtualmachine\"83-190ecf726ba4\",\"name";
67+
Assert.assertEquals(driver.getNestedVMPowerState(json), OutOfBandManagement.PowerState.Unknown);
68+
}
69+
70+
@Test
71+
public void testIsVMRunningEmptyJson() throws IOException {
72+
String json = "{}";
73+
Assert.assertEquals(driver.getNestedVMPowerState(json), OutOfBandManagement.PowerState.Unknown);
74+
}
75+
}

0 commit comments

Comments
 (0)