Skip to content

Commit f851091

Browse files
committed
Feature: Safely shutdown cloudstack
1 parent 434f15a commit f851091

27 files changed

Lines changed: 1018 additions & 177 deletions

File tree

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,10 @@ public class ApiConstants {
909909
public static final String LOGOUT = "logout";
910910
public static final String LIST_IDPS = "listIdps";
911911

912+
public static final String READY_FOR_SHUTDOWN = "readyforshutdown";
913+
public static final String SHUTDOWN_TRIGGERED = "shutdowntriggered";
914+
public static final String PENDING_JOBS_COUNT = "pendingjobscount";
915+
912916
public enum BootType {
913917
UEFI, BIOS;
914918

client/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,11 @@
513513
<artifactId>cloud-plugin-integrations-kubernetes-service</artifactId>
514514
<version>${project.version}</version>
515515
</dependency>
516+
<dependency>
517+
<groupId>org.apache.cloudstack</groupId>
518+
<artifactId>cloud-plugin-shutdown</artifactId>
519+
<version>${project.version}</version>
520+
</dependency>
516521
</dependencies>
517522
<build>
518523
<plugins>

core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@
255255
<bean class="org.apache.cloudstack.spring.lifecycle.registry.DumpRegistry" >
256256
<property name="registries" value="#{registryRegistry.registered}" />
257257
</bean>
258-
258+
259259
<bean id="registryRegistry"
260260
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
261261
</bean>
@@ -269,11 +269,11 @@
269269
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
270270
<property name="excludeKey" value="api.checkers.acl.exclude" />
271271
</bean>
272-
272+
273273
<bean id="querySelectorsRegistry"
274274
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
275275
<property name="excludeKey" value="query.selectors.exclude" />
276-
</bean>
276+
</bean>
277277

278278
<bean id="apiCommandsRegistry"
279279
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
@@ -284,7 +284,7 @@
284284
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
285285
<property name="excludeKey" value="hypervisor.gurus.exclude" />
286286
</bean>
287-
287+
288288
<bean id="vpcProvidersRegistry"
289289
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
290290
<property name="excludeKey" value="vpc.providers.exclude" />

framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/AsyncJobManager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,13 @@ void joinJob(long jobId, long joinJobId, String wakeupHandler, String wakupDispa
133133
List<AsyncJobVO> findFailureAsyncJobs(String... cmds);
134134

135135
long countPendingJobs(String havingInfo, String... cmds);
136+
137+
long countPendingNonPseudoJobs();
138+
139+
boolean isAllowAsyncJobs();
140+
141+
void disableAllowAsyncJobs();
142+
143+
void enableAllowAsyncJobs();
144+
136145
}

framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/dao/AsyncJobDao.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,7 @@ public interface AsyncJobDao extends GenericDao<AsyncJobVO, Long> {
4545

4646
List<AsyncJobVO> getFailureJobsSinceLastMsStart(long msId, String... cmds);
4747

48+
long countPendingNonPseudoJobs();
49+
4850
long countPendingJobs(String havingInfo, String... cmds);
4951
}

framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/dao/AsyncJobDaoImpl.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class AsyncJobDaoImpl extends GenericDaoBase<AsyncJobVO, Long> implements
4848
private final SearchBuilder<AsyncJobVO> expiringCompletedAsyncJobSearch;
4949
private final SearchBuilder<AsyncJobVO> failureMsidAsyncJobSearch;
5050
private final GenericSearchBuilder<AsyncJobVO, Long> asyncJobTypeSearch;
51+
private final GenericSearchBuilder<AsyncJobVO, Long> pendingNonPseudoAsyncJobsSearch;
5152

5253
public AsyncJobDaoImpl() {
5354
pendingAsyncJobSearch = createSearchBuilder();
@@ -103,6 +104,10 @@ public AsyncJobDaoImpl() {
103104
asyncJobTypeSearch.and("status", asyncJobTypeSearch.entity().getStatus(), SearchCriteria.Op.EQ);
104105
asyncJobTypeSearch.done();
105106

107+
pendingNonPseudoAsyncJobsSearch = createSearchBuilder(Long.class);
108+
pendingNonPseudoAsyncJobsSearch.select(null, SearchCriteria.Func.COUNT, pendingNonPseudoAsyncJobsSearch.entity().getId());
109+
pendingNonPseudoAsyncJobsSearch.and("instanceTypeNEQ", pendingNonPseudoAsyncJobsSearch.entity().getInstanceType(), SearchCriteria.Op.NEQ);
110+
pendingNonPseudoAsyncJobsSearch.and("jobStatusEQ", pendingNonPseudoAsyncJobsSearch.entity().getStatus(), SearchCriteria.Op.EQ);
106111
}
107112

108113
@Override
@@ -237,6 +242,15 @@ public List<AsyncJobVO> getFailureJobsSinceLastMsStart(long msId, String... cmds
237242
return listBy(sc);
238243
}
239244

245+
@Override
246+
public long countPendingNonPseudoJobs() {
247+
SearchCriteria<Long> sc = pendingNonPseudoAsyncJobsSearch.create();
248+
sc.setParameters("instanceTypeNEQ", AsyncJobVO.PSEUDO_JOB_INSTANCE_TYPE);
249+
sc.setParameters("jobStatusEQ", JobInfo.Status.IN_PROGRESS);
250+
List<Long> results = customSearch(sc, null);
251+
return results.get(0);
252+
}
253+
240254
@Override
241255
public long countPendingJobs(String havingInfo, String... cmds) {
242256
SearchCriteria<Long> sc = asyncJobTypeSearch.create();

framework/jobs/src/main/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ public class AsyncJobManagerImpl extends ManagerBase implements AsyncJobManager,
116116
private static final int HEARTBEAT_INTERVAL = 2000;
117117
private static final int GC_INTERVAL = 10000; // 10 seconds
118118

119+
private boolean allowAsyncJobs = true ;
120+
119121
@Inject
120122
private SyncQueueItemDao _queueItemDao;
121123
@Inject
@@ -192,6 +194,21 @@ public AsyncJob getPseudoJob(long accountId, long userId) {
192194
return job;
193195
}
194196

197+
@Override
198+
public boolean isAllowAsyncJobs() {
199+
return allowAsyncJobs;
200+
}
201+
202+
@Override
203+
public void disableAllowAsyncJobs() {
204+
this.allowAsyncJobs = false;
205+
}
206+
207+
@Override
208+
public void enableAllowAsyncJobs() {
209+
this.allowAsyncJobs = true;
210+
}
211+
195212
@Override
196213
public long submitAsyncJob(AsyncJob job) {
197214
return submitAsyncJob(job, false);
@@ -200,6 +217,9 @@ public long submitAsyncJob(AsyncJob job) {
200217
@SuppressWarnings("unchecked")
201218
@DB
202219
public long submitAsyncJob(AsyncJob job, boolean scheduleJobExecutionInContext) {
220+
if (!allowAsyncJobs) {
221+
throw new CloudRuntimeException("A shutdown has been triggered. Can not accept new jobs");
222+
}
203223
@SuppressWarnings("rawtypes")
204224
GenericDao dao = GenericDaoBase.getDao(job.getClass());
205225
job.setInitMsid(getMsid());
@@ -218,6 +238,10 @@ public long submitAsyncJob(AsyncJob job, boolean scheduleJobExecutionInContext)
218238
@Override
219239
@DB
220240
public long submitAsyncJob(final AsyncJob job, final String syncObjType, final long syncObjId) {
241+
if (!allowAsyncJobs) {
242+
throw new CloudRuntimeException("A shutdown has been triggered. Can not accept new jobs");
243+
}
244+
221245
try {
222246
@SuppressWarnings("rawtypes")
223247
final GenericDao dao = GenericDaoBase.getDao(job.getClass());
@@ -1171,4 +1195,9 @@ public List<AsyncJobVO> findFailureAsyncJobs(String... cmds) {
11711195
public long countPendingJobs(String havingInfo, String... cmds) {
11721196
return _jobDao.countPendingJobs(havingInfo, cmds);
11731197
}
1198+
1199+
@Override
1200+
public long countPendingNonPseudoJobs() {
1201+
return _jobDao.countPendingNonPseudoJobs();
1202+
}
11741203
}

plugins/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@
111111
<module>outofbandmanagement-drivers/nested-cloudstack</module>
112112
<module>outofbandmanagement-drivers/redfish</module>
113113

114+
<module>shutdown</module>
115+
114116
<module>storage/image/default</module>
115117
<module>storage/image/s3</module>
116118
<module>storage/image/sample</module>

plugins/shutdown/pom.xml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
<project xmlns="http://maven.apache.org/POM/4.0.0"
21+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
22+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
23+
<modelVersion>4.0.0</modelVersion>
24+
<artifactId>cloud-plugin-shutdown</artifactId>
25+
<name>Apache CloudStack Plugin - Safe Shutdown</name>
26+
<parent>
27+
<groupId>org.apache.cloudstack</groupId>
28+
<artifactId>cloudstack-plugins</artifactId>
29+
<version>4.18.0.0-SNAPSHOT</version>
30+
<relativePath>../pom.xml</relativePath>
31+
</parent>
32+
<dependencies>
33+
<dependency>
34+
<groupId>org.apache.cloudstack</groupId>
35+
<artifactId>cloud-api</artifactId>
36+
<version>${project.version}</version>
37+
</dependency>
38+
<dependency>
39+
<groupId>org.apache.cloudstack</groupId>
40+
<artifactId>cloud-utils</artifactId>
41+
<version>${project.version}</version>
42+
</dependency>
43+
</dependencies>
44+
</project>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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+
18+
package org.apache.cloudstack.api.command;
19+
20+
import javax.inject.Inject;
21+
22+
import org.apache.cloudstack.api.APICommand;
23+
import org.apache.cloudstack.api.BaseCmd;
24+
import org.apache.log4j.Logger;
25+
26+
import com.cloud.user.Account;
27+
28+
import org.apache.cloudstack.api.response.ReadyForShutdownResponse;
29+
import org.apache.cloudstack.shutdown.ShutdownManager;
30+
import org.apache.cloudstack.acl.RoleType;
31+
32+
@APICommand(name = CancelShutdownCmd.APINAME,
33+
description = "Cancels a triggered shutdown",
34+
responseObject = ReadyForShutdownResponse.class,
35+
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
36+
authorized = {RoleType.Admin})
37+
38+
public class CancelShutdownCmd extends BaseCmd {
39+
40+
public static final Logger LOG = Logger.getLogger(CancelShutdownCmd.class);
41+
public static final String APINAME = "cancelShutdown";
42+
43+
@Inject
44+
private ShutdownManager shutdownManager;
45+
46+
@Override
47+
public String getCommandName() {
48+
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
49+
}
50+
51+
@Override
52+
public long getEntityOwnerId() {
53+
return Account.ACCOUNT_ID_SYSTEM;
54+
}
55+
56+
@Override
57+
public void execute() {
58+
final ReadyForShutdownResponse response = shutdownManager.cancelShutdown();
59+
response.setResponseName(getCommandName());
60+
response.setObjectName("cancelshutdown");
61+
setResponseObject(response);
62+
}
63+
64+
}

0 commit comments

Comments
 (0)