mirror of https://github.com/apache/cloudstack.git
Merge cc2d5df079 into 5893ba5a8c
This commit is contained in:
commit
671366622e
|
|
@ -43,7 +43,7 @@ import org.apache.cloudstack.storage.object.Bucket;
|
|||
import org.apache.cloudstack.storage.object.ObjectStore;
|
||||
import org.apache.cloudstack.storage.sharedfs.SharedFS;
|
||||
import org.apache.cloudstack.usage.Usage;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
import org.apache.cloudstack.schedule.ResourceSchedule;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.DataCenterGuestIpv6Prefix;
|
||||
|
|
@ -125,17 +125,18 @@ public class EventTypes {
|
|||
public static final String EVENT_VM_UNMANAGE = "VM.UNMANAGE";
|
||||
public static final String EVENT_VM_RECOVER = "VM.RECOVER";
|
||||
|
||||
// VM Schedule
|
||||
public static final String EVENT_VM_SCHEDULE_CREATE = "VM.SCHEDULE.CREATE";
|
||||
public static final String EVENT_VM_SCHEDULE_UPDATE = "VM.SCHEDULE.UPDATE";
|
||||
public static final String EVENT_VM_SCHEDULE_DELETE = "VM.SCHEDULE.DELETE";
|
||||
|
||||
// VM Schedule action-execution events (fired when a scheduled action runs).
|
||||
public static final String EVENT_VM_SCHEDULE_START = "VM.SCHEDULE.START";
|
||||
public static final String EVENT_VM_SCHEDULE_STOP = "VM.SCHEDULE.STOP";
|
||||
public static final String EVENT_VM_SCHEDULE_REBOOT = "VM.SCHEDULE.REBOOT";
|
||||
public static final String EVENT_VM_SCHEDULE_FORCE_STOP = "VM.SCHEDULE.FORCE.STOP";
|
||||
public static final String EVENT_VM_SCHEDULE_FORCE_REBOOT = "VM.SCHEDULE.FORCE.REBOOT";
|
||||
|
||||
// Generic Resource Schedule CRUD events (apply to all resource types).
|
||||
public static final String EVENT_SCHEDULE_CREATE = "SCHEDULE.CREATE";
|
||||
public static final String EVENT_SCHEDULE_UPDATE = "SCHEDULE.UPDATE";
|
||||
public static final String EVENT_SCHEDULE_DELETE = "SCHEDULE.DELETE";
|
||||
|
||||
// Domain Router
|
||||
public static final String EVENT_ROUTER_CREATE = "ROUTER.CREATE";
|
||||
public static final String EVENT_ROUTER_DESTROY = "ROUTER.DESTROY";
|
||||
|
|
@ -888,15 +889,17 @@ public class EventTypes {
|
|||
entityEventDetails.put(EVENT_VM_IMPORT, VirtualMachine.class);
|
||||
entityEventDetails.put(EVENT_VM_UNMANAGE, VirtualMachine.class);
|
||||
|
||||
// VMSchedule
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_CREATE, VMSchedule.class);
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_DELETE, VMSchedule.class);
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_UPDATE, VMSchedule.class);
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_START, VMSchedule.class);
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_STOP, VMSchedule.class);
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_REBOOT, VMSchedule.class);
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_STOP, VMSchedule.class);
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_REBOOT, VMSchedule.class);
|
||||
// VMSchedule action-execution events
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_START, ResourceSchedule.class);
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_STOP, ResourceSchedule.class);
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_REBOOT, ResourceSchedule.class);
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_STOP, ResourceSchedule.class);
|
||||
entityEventDetails.put(EVENT_VM_SCHEDULE_FORCE_REBOOT, ResourceSchedule.class);
|
||||
|
||||
// Generic Resource Schedule
|
||||
entityEventDetails.put(EVENT_SCHEDULE_CREATE, ResourceSchedule.class);
|
||||
entityEventDetails.put(EVENT_SCHEDULE_UPDATE, ResourceSchedule.class);
|
||||
entityEventDetails.put(EVENT_SCHEDULE_DELETE, ResourceSchedule.class);
|
||||
|
||||
entityEventDetails.put(EVENT_ROUTER_CREATE, VirtualRouter.class);
|
||||
entityEventDetails.put(EVENT_ROUTER_DESTROY, VirtualRouter.class);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,132 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.schedule;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
@APICommand(name = "createResourceSchedule", description = "Create Resource Schedule", responseObject = ResourceScheduleResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class CreateResourceScheduleCmd extends BaseCmd {
|
||||
|
||||
@Inject
|
||||
ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, description = "Type of the resource")
|
||||
private String resourceType;
|
||||
|
||||
@Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, required = true, description = "ID of the resource for which schedule is to be defined")
|
||||
private String resourceId;
|
||||
|
||||
@Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, required = false, description = "Description of the schedule")
|
||||
private String description;
|
||||
|
||||
@Parameter(name = ApiConstants.SCHEDULE, type = CommandType.STRING, required = true, description = "Schedule for action on resource in cron format. e.g. '0 15 10 * *' for 'at 15:00 on 10th day of every month'")
|
||||
private String schedule;
|
||||
|
||||
@Parameter(name = ApiConstants.TIMEZONE, type = CommandType.STRING, required = true, description = "Specifies a timezone for this command. For more information on the timezone parameter, see TimeZone Format.")
|
||||
private String timeZone;
|
||||
|
||||
@Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, required = true, description = "Action to take on the resource.")
|
||||
private String action;
|
||||
|
||||
@Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = false, description = "Start date from which the schedule becomes active. Defaults to current date plus 1 minute. Use format \"yyyy-MM-dd hh:mm:ss\"")
|
||||
private Date startDate;
|
||||
|
||||
@Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, required = false, description = "End date after which the schedule becomes inactive. Use format \"yyyy-MM-dd hh:mm:ss\"")
|
||||
private Date endDate;
|
||||
|
||||
@Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, description = "Enable schedule. Defaults to true")
|
||||
private Boolean enabled;
|
||||
|
||||
@Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = false, description = "Map of (key/value pairs) details for the schedule.")
|
||||
private Map details;
|
||||
|
||||
public ApiCommandResourceType getResourceType() {
|
||||
ApiCommandResourceType type = EnumUtils.getEnumIgnoreCase(ApiCommandResourceType.class, resourceType);
|
||||
if (type == null) {
|
||||
throw new InvalidParameterValueException("Unknown resource type: " + resourceType);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getSchedule() {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
public String getTimeZone() {
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public Date getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public Date getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
if (enabled == null) {
|
||||
enabled = true;
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public Map<String, String> getDetails() {
|
||||
return convertDetailsToMap(details);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
ResourceScheduleResponse response = resourceScheduleManager.createSchedule(getResourceType(), getResourceId(),
|
||||
getDescription(), getSchedule(), getTimeZone(), getAction(), getStartDate(), getEndDate(), getEnabled(), getDetails());
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getAccountId();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.schedule;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
@APICommand(name = "deleteResourceSchedule", description = "Delete Resource Schedule", responseObject = SuccessResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class DeleteResourceScheduleCmd extends BaseCmd {
|
||||
|
||||
@Inject
|
||||
ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, description = "Type of the resource")
|
||||
private String resourceType;
|
||||
|
||||
@Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, required = true, description = "ID of the resource for which schedules are to be deleted")
|
||||
private String resourceId;
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ResourceScheduleResponse.class, required = false, description = "ID of the schedule to be deleted")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ResourceScheduleResponse.class, required = false, description = "comma separated list of schedule ids to be deleted")
|
||||
private List<Long> ids;
|
||||
|
||||
public ApiCommandResourceType getResourceType() {
|
||||
ApiCommandResourceType type = EnumUtils.getEnumIgnoreCase(ApiCommandResourceType.class, resourceType);
|
||||
if (type == null) {
|
||||
throw new InvalidParameterValueException("Unknown resource type: " + resourceType);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<Long> getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
resourceScheduleManager.removeSchedule(getResourceType(), getResourceId(), getId(), getIds());
|
||||
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getAccountId();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.schedule;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseListCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
@APICommand(name = "listResourceSchedule", description = "List Resource Schedules", responseObject = ResourceScheduleResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class ListResourceScheduleCmd extends BaseListCmd {
|
||||
|
||||
@Inject
|
||||
ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ResourceScheduleResponse.class, required = false, description = "ID of the schedule")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = ResourceScheduleResponse.class, required = false, description = "comma separated list of schedule ids")
|
||||
private List<Long> ids;
|
||||
|
||||
@Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, description = "Type of the resource.")
|
||||
private String resourceType;
|
||||
|
||||
@Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, required = true, description = "ID of the resource for which schedules are to be listed.")
|
||||
private String resourceId;
|
||||
|
||||
@Parameter(name = ApiConstants.ACTION, type = CommandType.STRING, required = false, description = "Action to take on the resource.")
|
||||
private String action;
|
||||
|
||||
@Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, description = "Filter by enabled status.")
|
||||
private Boolean enabled;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<Long> getIds() {
|
||||
return ids;
|
||||
}
|
||||
|
||||
public ApiCommandResourceType getResourceType() {
|
||||
ApiCommandResourceType type = EnumUtils.getEnumIgnoreCase(ApiCommandResourceType.class, resourceType);
|
||||
if (type == null) {
|
||||
throw new InvalidParameterValueException("Unknown resource type: " + resourceType);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
ListResponse<ResourceScheduleResponse> response = resourceScheduleManager.listSchedule(
|
||||
getId(), getIds(), getResourceType(), getResourceId(), getAction(), getEnabled(),
|
||||
getStartIndex(), getPageSizeVal()
|
||||
);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.api.command.user.schedule;
|
||||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
@APICommand(name = "updateResourceSchedule", description = "Update Resource Schedule", responseObject = ResourceScheduleResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.23.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class UpdateResourceScheduleCmd extends BaseCmd {
|
||||
|
||||
@Inject
|
||||
ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ResourceScheduleResponse.class, required = true, description = "ID of the schedule to be updated")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, required = false, description = "Description of the schedule")
|
||||
private String description;
|
||||
|
||||
@Parameter(name = ApiConstants.SCHEDULE, type = CommandType.STRING, required = false, description = "Schedule for action on resource in cron format.")
|
||||
private String schedule;
|
||||
|
||||
@Parameter(name = ApiConstants.TIMEZONE, type = CommandType.STRING, required = false, description = "Specifies a timezone for this command.")
|
||||
private String timeZone;
|
||||
|
||||
@Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, required = false, description = "Start date from which the schedule becomes active.")
|
||||
private Date startDate;
|
||||
|
||||
@Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, required = false, description = "End date after which the schedule becomes inactive.")
|
||||
private Date endDate;
|
||||
|
||||
@Parameter(name = ApiConstants.ENABLED, type = CommandType.BOOLEAN, required = false, description = "Enable or disable the schedule.")
|
||||
private Boolean enabled;
|
||||
|
||||
@Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = false, description = "Map of (key/value pairs) details for the schedule.")
|
||||
private Map details;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getSchedule() {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
public String getTimeZone() {
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
public Date getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public Date getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public Map<String, String> getDetails() {
|
||||
return convertDetailsToMap(details);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
ResourceScheduleResponse response = resourceScheduleManager.updateSchedule(getId(), getDescription(), getSchedule(),
|
||||
getTimeZone(), getStartDate(), getEndDate(), getEnabled(), getDetails());
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return CallContext.current().getCallingAccount().getAccountId();
|
||||
}
|
||||
}
|
||||
|
|
@ -22,23 +22,26 @@ import com.cloud.exception.InvalidParameterValueException;
|
|||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Date;
|
||||
|
||||
@Deprecated
|
||||
@APICommand(name = "createVMSchedule", description = "Create Instance Schedule", responseObject = VMScheduleResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class CreateVMScheduleCmd extends BaseCmd {
|
||||
|
||||
@Inject
|
||||
VMScheduleManager vmScheduleManager;
|
||||
ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
|
||||
type = CommandType.UUID,
|
||||
|
|
@ -75,14 +78,14 @@ public class CreateVMScheduleCmd extends BaseCmd {
|
|||
type = CommandType.DATE,
|
||||
required = false,
|
||||
description = "Start date from which the schedule becomes active. Defaults to current date plus 1 minute."
|
||||
+ "Use format \"yyyy-MM-dd hh:mm:ss\")")
|
||||
+ "Use format \"yyyy-MM-dd hh:mm:ss\"")
|
||||
private Date startDate;
|
||||
|
||||
@Parameter(name = ApiConstants.END_DATE,
|
||||
type = CommandType.DATE,
|
||||
required = false,
|
||||
description = "End date after which the schedule becomes inactive"
|
||||
+ "Use format \"yyyy-MM-dd hh:mm:ss\")")
|
||||
+ "Use format \"yyyy-MM-dd hh:mm:ss\"")
|
||||
private Date endDate;
|
||||
|
||||
@Parameter(name = ApiConstants.ENABLED,
|
||||
|
|
@ -91,9 +94,9 @@ public class CreateVMScheduleCmd extends BaseCmd {
|
|||
description = "Enable Instance schedule. Defaults to true")
|
||||
private Boolean enabled;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
/// //////////////// Accessors ///////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
|
||||
public Long getVmId() {
|
||||
return vmId;
|
||||
|
|
@ -130,13 +133,19 @@ public class CreateVMScheduleCmd extends BaseCmd {
|
|||
return enabled;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
/// //////////// API Implementation///////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
VMScheduleResponse response = vmScheduleManager.createSchedule(this);
|
||||
String resourceIdStr = getVmId() != null ? String.valueOf(getVmId()) : null;
|
||||
|
||||
ResourceScheduleResponse scheduleResponse = resourceScheduleManager.createSchedule(
|
||||
ApiCommandResourceType.VirtualMachine,
|
||||
resourceIdStr, getDescription(), getSchedule(), getTimeZone(), getAction(),
|
||||
getStartDate(), getEndDate(), getEnabled(), null);
|
||||
VMScheduleResponse response = new VMScheduleResponse(scheduleResponse);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.cloud.exception.InvalidParameterValueException;
|
|||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
|
|
@ -30,19 +31,19 @@ import org.apache.cloudstack.api.ServerApiException;
|
|||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Deprecated
|
||||
@APICommand(name = "deleteVMSchedule", description = "Delete Instance Schedule.", responseObject = SuccessResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class DeleteVMScheduleCmd extends BaseCmd {
|
||||
@Inject
|
||||
VMScheduleManager vmScheduleManager;
|
||||
ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
|
||||
type = CommandType.UUID,
|
||||
|
|
@ -50,12 +51,14 @@ public class DeleteVMScheduleCmd extends BaseCmd {
|
|||
required = true,
|
||||
description = "ID of Instance")
|
||||
private Long vmId;
|
||||
|
||||
@Parameter(name = ApiConstants.ID,
|
||||
type = CommandType.UUID,
|
||||
entityType = VMScheduleResponse.class,
|
||||
required = false,
|
||||
description = "ID of Instance schedule")
|
||||
private Long id;
|
||||
|
||||
@Parameter(name = ApiConstants.IDS,
|
||||
type = CommandType.LIST,
|
||||
collectionType = CommandType.UUID,
|
||||
|
|
@ -64,9 +67,9 @@ public class DeleteVMScheduleCmd extends BaseCmd {
|
|||
description = "IDs of Instance schedule")
|
||||
private List<Long> ids;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
/// //////////////// Accessors ///////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
|
|
@ -83,18 +86,21 @@ public class DeleteVMScheduleCmd extends BaseCmd {
|
|||
return vmId;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
/// //////////// API Implementation///////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
long rowsRemoved = vmScheduleManager.removeSchedule(this);
|
||||
String resourceIdStr = getVmId() != null ? String.valueOf(getVmId()) : null;
|
||||
long rowsRemoved = resourceScheduleManager.removeSchedule(
|
||||
ApiCommandResourceType.VirtualMachine,
|
||||
resourceIdStr, getId(), getIds());
|
||||
|
||||
if (rowsRemoved > 0) {
|
||||
final SuccessResponse response = new SuccessResponse();
|
||||
response.setResponseName(getCommandName());
|
||||
response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase());
|
||||
response.setObjectName("vmschedule");
|
||||
setResponseObject(response);
|
||||
} else {
|
||||
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Instance Schedules");
|
||||
|
|
|
|||
|
|
@ -20,23 +20,27 @@ package org.apache.cloudstack.api.command.user.vm;
|
|||
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseListCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.api.response.UserVmResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Deprecated
|
||||
@APICommand(name = "listVMSchedule", description = "List Instance Schedules.", responseObject = VMScheduleResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class ListVMScheduleCmd extends BaseListCmd {
|
||||
@Inject
|
||||
VMScheduleManager vmScheduleManager;
|
||||
ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
|
||||
type = CommandType.UUID,
|
||||
|
|
@ -61,12 +65,12 @@ public class ListVMScheduleCmd extends BaseListCmd {
|
|||
@Parameter(name = ApiConstants.ENABLED,
|
||||
type = CommandType.BOOLEAN,
|
||||
required = false,
|
||||
description = "ID of Instance schedule")
|
||||
description = "Filter by enabled status")
|
||||
private Boolean enabled;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
/// //////////////// Accessors ///////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
|
||||
public Long getVmId() {
|
||||
return vmId;
|
||||
|
|
@ -84,14 +88,26 @@ public class ListVMScheduleCmd extends BaseListCmd {
|
|||
return enabled;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
/// //////////// API Implementation///////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
@Override
|
||||
public void execute() {
|
||||
ListResponse<VMScheduleResponse> response = vmScheduleManager.listSchedule(this);
|
||||
String resourceIdStr = getVmId() != null ? String.valueOf(getVmId()) : null;
|
||||
|
||||
ListResponse<ResourceScheduleResponse> scheduleResponse = resourceScheduleManager.listSchedule(
|
||||
getId(), null, ApiCommandResourceType.VirtualMachine, resourceIdStr, getAction(), getEnabled(),
|
||||
getStartIndex(), getPageSizeVal()
|
||||
);
|
||||
|
||||
List<VMScheduleResponse> vmScheduleResponses = new ArrayList<>();
|
||||
for (ResourceScheduleResponse resourceScheduleResponse : scheduleResponse.getResponses()) {
|
||||
vmScheduleResponses.add(new VMScheduleResponse(resourceScheduleResponse));
|
||||
}
|
||||
ListResponse<VMScheduleResponse> response = new ListResponse<>();
|
||||
response.setResponses(vmScheduleResponses, scheduleResponse.getCount());
|
||||
response.setResponseName(getCommandName());
|
||||
response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase());
|
||||
response.setObjectName("vmschedule");
|
||||
setResponseObject(response);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,22 +22,25 @@ import com.cloud.exception.InvalidParameterValueException;
|
|||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
|
||||
import org.apache.cloudstack.schedule.ResourceSchedule;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Date;
|
||||
|
||||
@Deprecated
|
||||
@APICommand(name = "updateVMSchedule", description = "Update Instance Schedule.", responseObject = VMScheduleResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0",
|
||||
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
|
||||
public class UpdateVMScheduleCmd extends BaseCmd {
|
||||
@Inject
|
||||
VMScheduleManager vmScheduleManager;
|
||||
ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@Parameter(name = ApiConstants.ID,
|
||||
type = CommandType.UUID,
|
||||
|
|
@ -68,14 +71,14 @@ public class UpdateVMScheduleCmd extends BaseCmd {
|
|||
type = CommandType.DATE,
|
||||
required = false,
|
||||
description = "Start date from which the schedule becomes active"
|
||||
+ "Use format \"yyyy-MM-dd hh:mm:ss\")")
|
||||
+ "Use format \"yyyy-MM-dd hh:mm:ss\"")
|
||||
private Date startDate;
|
||||
|
||||
@Parameter(name = ApiConstants.END_DATE,
|
||||
type = CommandType.DATE,
|
||||
required = false,
|
||||
description = "End date after which the schedule becomes inactive"
|
||||
+ "Use format \"yyyy-MM-dd hh:mm:ss\")")
|
||||
+ "Use format \"yyyy-MM-dd hh:mm:ss\"")
|
||||
private Date endDate;
|
||||
|
||||
@Parameter(name = ApiConstants.ENABLED,
|
||||
|
|
@ -84,9 +87,9 @@ public class UpdateVMScheduleCmd extends BaseCmd {
|
|||
description = "Enable Instance schedule")
|
||||
private Boolean enabled;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
/// //////////////// Accessors ///////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
|
|
@ -116,24 +119,29 @@ public class UpdateVMScheduleCmd extends BaseCmd {
|
|||
return enabled;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
/// //////////// API Implementation///////////////////
|
||||
/// //////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
VMScheduleResponse response = vmScheduleManager.updateSchedule(this);
|
||||
ResourceScheduleResponse scheduleResponse = resourceScheduleManager.updateSchedule(
|
||||
getId(), getDescription(), getSchedule(), getTimeZone(), getStartDate(), getEndDate(), getEnabled(), null);
|
||||
VMScheduleResponse response = new VMScheduleResponse(scheduleResponse);
|
||||
response.setResponseName(getCommandName());
|
||||
setResponseObject(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
VMSchedule vmSchedule = _entityMgr.findById(VMSchedule.class, getId());
|
||||
if (vmSchedule == null) {
|
||||
throw new InvalidParameterValueException(String.format("Unable to find vmSchedule by id=%d", getId()));
|
||||
ResourceSchedule schedule = _entityMgr.findById(ResourceSchedule.class, getId());
|
||||
if (schedule == null || !ApiCommandResourceType.VirtualMachine.equals(schedule.getResourceType())) {
|
||||
throw new InvalidParameterValueException(String.format("Unable to find VM schedule by id=%d", getId()));
|
||||
}
|
||||
VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, schedule.getResourceId());
|
||||
if (vm == null) {
|
||||
throw new InvalidParameterValueException(String.format("Unable to find VM schedule by id=%d", getId()));
|
||||
}
|
||||
VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, vmSchedule.getVmId());
|
||||
return vm.getAccountId();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.api.response;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
import org.apache.cloudstack.api.EntityReference;
|
||||
import org.apache.cloudstack.schedule.ResourceSchedule;
|
||||
|
||||
import com.cloud.serializer.Param;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
@EntityReference(value = ResourceSchedule.class)
|
||||
public class ResourceScheduleResponse extends BaseResponse {
|
||||
@SerializedName(ApiConstants.ID)
|
||||
@Param(description = "The ID of resource schedule")
|
||||
private String id;
|
||||
|
||||
@SerializedName(ApiConstants.RESOURCE_TYPE)
|
||||
@Param(description = "Type of the resource")
|
||||
private ApiCommandResourceType resourceType;
|
||||
|
||||
@SerializedName(ApiConstants.RESOURCE_ID)
|
||||
@Param(description = "ID of the resource")
|
||||
private String resourceId;
|
||||
|
||||
@SerializedName(ApiConstants.DESCRIPTION)
|
||||
@Param(description = "Description of resource schedule")
|
||||
private String description;
|
||||
|
||||
@SerializedName(ApiConstants.SCHEDULE)
|
||||
@Param(description = "Cron formatted resource schedule")
|
||||
private String schedule;
|
||||
|
||||
@SerializedName(ApiConstants.TIMEZONE)
|
||||
@Param(description = "Timezone of the schedule")
|
||||
private String timeZone;
|
||||
|
||||
@SerializedName(ApiConstants.ACTION)
|
||||
@Param(description = "Action")
|
||||
private ResourceSchedule.Action action;
|
||||
|
||||
@SerializedName(ApiConstants.ENABLED)
|
||||
@Param(description = "Resource schedule is enabled")
|
||||
private boolean enabled;
|
||||
|
||||
@SerializedName(ApiConstants.START_DATE)
|
||||
@Param(description = "Date from which the schedule is active")
|
||||
private Date startDate;
|
||||
|
||||
@SerializedName(ApiConstants.END_DATE)
|
||||
@Param(description = "Date after which the schedule becomes inactive")
|
||||
private Date endDate;
|
||||
|
||||
@SerializedName(ApiConstants.DETAILS)
|
||||
@Param(description = "Schedule details")
|
||||
private Map<String, String> details;
|
||||
|
||||
@SerializedName(ApiConstants.CREATED)
|
||||
@Param(description = "Date when the schedule was created")
|
||||
private Date created;
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setResourceType(ApiCommandResourceType resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
public ApiCommandResourceType getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setResourceId(String resourceId) {
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public String getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setSchedule(String schedule) {
|
||||
this.schedule = schedule;
|
||||
}
|
||||
|
||||
public String getSchedule() {
|
||||
return schedule;
|
||||
}
|
||||
|
||||
public void setTimeZone(String timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
public String getTimeZone() {
|
||||
return timeZone;
|
||||
}
|
||||
|
||||
public void setAction(ResourceSchedule.Action action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public ResourceSchedule.Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setStartDate(Date startDate) {
|
||||
this.startDate = startDate;
|
||||
}
|
||||
|
||||
public Date getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public void setEndDate(Date endDate) {
|
||||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public Date getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public void setDetails(Map<String, String> details) {
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
public Map<String, String> getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
}
|
||||
|
|
@ -23,11 +23,11 @@ import com.google.gson.annotations.SerializedName;
|
|||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
import org.apache.cloudstack.api.EntityReference;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
import org.apache.cloudstack.schedule.ResourceSchedule;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@EntityReference(value = VMSchedule.class)
|
||||
@EntityReference(value = ResourceSchedule.class)
|
||||
public class VMScheduleResponse extends BaseResponse {
|
||||
@SerializedName(ApiConstants.ID)
|
||||
@Param(description = "The ID of Instance schedule")
|
||||
|
|
@ -51,7 +51,7 @@ public class VMScheduleResponse extends BaseResponse {
|
|||
|
||||
@SerializedName(ApiConstants.ACTION)
|
||||
@Param(description = "Action")
|
||||
private VMSchedule.Action action;
|
||||
private ResourceSchedule.Action action;
|
||||
|
||||
@SerializedName(ApiConstants.ENABLED)
|
||||
@Param(description = "VM schedule is enabled")
|
||||
|
|
@ -69,6 +69,20 @@ public class VMScheduleResponse extends BaseResponse {
|
|||
@Param(description = "Date when the schedule was created")
|
||||
private Date created;
|
||||
|
||||
public VMScheduleResponse(ResourceScheduleResponse scheduleResponse) {
|
||||
super("vmschedule");
|
||||
this.id = scheduleResponse.getId();
|
||||
this.vmId = scheduleResponse.getResourceId();
|
||||
this.description = scheduleResponse.getDescription();
|
||||
this.schedule = scheduleResponse.getSchedule();
|
||||
this.timeZone = scheduleResponse.getTimeZone();
|
||||
this.action = scheduleResponse.getAction();
|
||||
this.enabled = scheduleResponse.getEnabled();
|
||||
this.startDate = scheduleResponse.getStartDate();
|
||||
this.endDate = scheduleResponse.getEndDate();
|
||||
this.created = scheduleResponse.getCreated();
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
|
@ -89,7 +103,7 @@ public class VMScheduleResponse extends BaseResponse {
|
|||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
public void setAction(VMSchedule.Action action) {
|
||||
public void setAction(ResourceSchedule.Action action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
|
|
@ -105,5 +119,7 @@ public class VMScheduleResponse extends BaseResponse {
|
|||
this.endDate = endDate;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {this.created = created;}
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,20 +16,31 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule;
|
||||
package org.apache.cloudstack.schedule;
|
||||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.util.Date;
|
||||
|
||||
public interface VMSchedule extends Identity, InternalIdentity {
|
||||
enum Action {
|
||||
START, STOP, REBOOT, FORCE_STOP, FORCE_REBOOT
|
||||
public interface ResourceSchedule extends Identity, InternalIdentity {
|
||||
|
||||
/**
|
||||
* Common contract for scheduler actions. Each provider defines its own enum
|
||||
* implementing this interface so the generic machinery can call {@link #name()}
|
||||
* and {@link #getEventType()} without knowing the concrete type.
|
||||
*/
|
||||
interface Action {
|
||||
String name();
|
||||
|
||||
String getEventType();
|
||||
}
|
||||
|
||||
long getVmId();
|
||||
ApiCommandResourceType getResourceType();
|
||||
|
||||
long getResourceId();
|
||||
|
||||
String getDescription();
|
||||
|
||||
|
|
@ -37,7 +48,7 @@ public interface VMSchedule extends Identity, InternalIdentity {
|
|||
|
||||
String getTimeZone();
|
||||
|
||||
Action getAction();
|
||||
String getActionName();
|
||||
|
||||
boolean getEnabled();
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.schedule;
|
||||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ResourceScheduleManager {
|
||||
|
||||
ResourceScheduleResponse createSchedule(ApiCommandResourceType resourceType, String resourceUuid,
|
||||
String description, String schedule, String timeZone, String action,
|
||||
Date startDate, Date endDate, boolean enabled, Map<String, String> details);
|
||||
|
||||
ResourceScheduleResponse updateSchedule(Long id, String description, String schedule, String timeZone,
|
||||
Date startDate, Date endDate, Boolean enabled, Map<String, String> details);
|
||||
|
||||
ListResponse<ResourceScheduleResponse> listSchedule(Long id, List<Long> ids, ApiCommandResourceType resourceType,
|
||||
String resourceUuid, String action, Boolean enabled,
|
||||
Long startIndex, Long pageSize);
|
||||
|
||||
Long removeSchedule(ApiCommandResourceType resourceType, String resourceUuid, Long id, List<Long> ids);
|
||||
|
||||
void removeSchedulesForResource(ApiCommandResourceType resourceType, long resourceId);
|
||||
}
|
||||
|
|
@ -16,23 +16,26 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule;
|
||||
package org.apache.cloudstack.schedule;
|
||||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface VMScheduledJob extends Identity, InternalIdentity {
|
||||
long getVmId();
|
||||
public interface ResourceScheduledJob extends Identity, InternalIdentity {
|
||||
ApiCommandResourceType getResourceType();
|
||||
|
||||
long getVmScheduleId();
|
||||
long getResourceId();
|
||||
|
||||
long getScheduleId();
|
||||
|
||||
Long getAsyncJobId();
|
||||
|
||||
void setAsyncJobId(long asyncJobId);
|
||||
|
||||
VMSchedule.Action getAction();
|
||||
String getActionName();
|
||||
|
||||
Date getScheduledTime();
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.schedule.vm;
|
||||
|
||||
import com.cloud.event.EventTypes;
|
||||
import org.apache.cloudstack.schedule.ResourceSchedule;
|
||||
|
||||
public enum VMScheduleAction implements ResourceSchedule.Action {
|
||||
START {
|
||||
@Override
|
||||
public String getEventType() { return EventTypes.EVENT_VM_SCHEDULE_START; }
|
||||
},
|
||||
STOP {
|
||||
@Override
|
||||
public String getEventType() { return EventTypes.EVENT_VM_SCHEDULE_STOP; }
|
||||
},
|
||||
REBOOT {
|
||||
@Override
|
||||
public String getEventType() { return EventTypes.EVENT_VM_SCHEDULE_REBOOT; }
|
||||
},
|
||||
FORCE_STOP {
|
||||
@Override
|
||||
public String getEventType() { return EventTypes.EVENT_VM_SCHEDULE_FORCE_STOP; }
|
||||
},
|
||||
FORCE_REBOOT {
|
||||
@Override
|
||||
public String getEventType() { return EventTypes.EVENT_VM_SCHEDULE_FORCE_REBOOT; }
|
||||
};
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule;
|
||||
|
||||
import org.apache.cloudstack.api.command.user.vm.CreateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.DeleteVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.ListVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.UpdateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
|
||||
public interface VMScheduleManager {
|
||||
VMScheduleResponse createSchedule(CreateVMScheduleCmd createVMScheduleCmd);
|
||||
|
||||
VMScheduleResponse createResponse(VMSchedule vmSchedule);
|
||||
|
||||
ListResponse<VMScheduleResponse> listSchedule(ListVMScheduleCmd listVMScheduleCmd);
|
||||
|
||||
VMScheduleResponse updateSchedule(UpdateVMScheduleCmd updateVMScheduleCmd);
|
||||
|
||||
long removeScheduleByVmId(long vmId, boolean expunge);
|
||||
|
||||
Long removeSchedule(DeleteVMScheduleCmd deleteVMScheduleCmd);
|
||||
}
|
||||
|
|
@ -21,8 +21,9 @@ package org.apache.cloudstack.api.command.user.vm;
|
|||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
|
@ -34,9 +35,11 @@ import org.mockito.MockitoAnnotations;
|
|||
|
||||
public class CreateVMScheduleCmdTest {
|
||||
@Mock
|
||||
public VMScheduleManager vmScheduleManager;
|
||||
public ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@Mock
|
||||
public EntityManager entityManager;
|
||||
|
||||
@InjectMocks
|
||||
private CreateVMScheduleCmd createVMScheduleCmd = new CreateVMScheduleCmd();
|
||||
|
||||
|
|
@ -59,10 +62,19 @@ public class CreateVMScheduleCmdTest {
|
|||
*/
|
||||
@Test
|
||||
public void testSuccessfulExecution() {
|
||||
VMScheduleResponse vmScheduleResponse = Mockito.mock(VMScheduleResponse.class);
|
||||
Mockito.when(vmScheduleManager.createSchedule(createVMScheduleCmd)).thenReturn(vmScheduleResponse);
|
||||
ResourceScheduleResponse scheduleResponse = new ResourceScheduleResponse();
|
||||
scheduleResponse.setId("schedule-uuid");
|
||||
scheduleResponse.setResourceId("vm-uuid");
|
||||
Mockito.when(resourceScheduleManager.createSchedule(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.anyBoolean(), Mockito.any()
|
||||
)).thenReturn(scheduleResponse);
|
||||
createVMScheduleCmd.execute();
|
||||
Assert.assertEquals(vmScheduleResponse, createVMScheduleCmd.getResponseObject());
|
||||
VMScheduleResponse response = (VMScheduleResponse) createVMScheduleCmd.getResponseObject();
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals("schedule-uuid", org.springframework.test.util.ReflectionTestUtils.getField(response, "id"));
|
||||
Assert.assertEquals("vm-uuid", org.springframework.test.util.ReflectionTestUtils.getField(response, "vmId"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -72,7 +84,11 @@ public class CreateVMScheduleCmdTest {
|
|||
*/
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testInvalidParameterValueException() {
|
||||
Mockito.when(vmScheduleManager.createSchedule(createVMScheduleCmd)).thenThrow(InvalidParameterValueException.class);
|
||||
Mockito.when(resourceScheduleManager.createSchedule(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.anyBoolean(), Mockito.any()
|
||||
)).thenThrow(new InvalidParameterValueException("Invalid schedule"));
|
||||
createVMScheduleCmd.execute();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ import com.cloud.utils.db.EntityManager;
|
|||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
|
@ -36,7 +35,8 @@ import org.mockito.MockitoAnnotations;
|
|||
|
||||
public class DeleteVMScheduleCmdTest {
|
||||
@Mock
|
||||
public VMScheduleManager vmScheduleManager;
|
||||
public ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@Mock
|
||||
public EntityManager entityManager;
|
||||
|
||||
|
|
@ -64,9 +64,11 @@ public class DeleteVMScheduleCmdTest {
|
|||
public void testSuccessfulExecution() {
|
||||
final SuccessResponse response = new SuccessResponse();
|
||||
response.setResponseName(deleteVMScheduleCmd.getCommandName());
|
||||
response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase());
|
||||
response.setObjectName("vmschedule");
|
||||
|
||||
Mockito.when(vmScheduleManager.removeSchedule(deleteVMScheduleCmd)).thenReturn(1L);
|
||||
Mockito.when(resourceScheduleManager.removeSchedule(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()
|
||||
)).thenReturn(1L);
|
||||
deleteVMScheduleCmd.execute();
|
||||
SuccessResponse actualResponse = (SuccessResponse) deleteVMScheduleCmd.getResponseObject();
|
||||
Assert.assertEquals(response.getResponseName(), actualResponse.getResponseName());
|
||||
|
|
@ -80,7 +82,9 @@ public class DeleteVMScheduleCmdTest {
|
|||
*/
|
||||
@Test(expected = ServerApiException.class)
|
||||
public void testServerApiException() {
|
||||
Mockito.when(vmScheduleManager.removeSchedule(deleteVMScheduleCmd)).thenReturn(0L);
|
||||
Mockito.when(resourceScheduleManager.removeSchedule(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()
|
||||
)).thenReturn(0L);
|
||||
deleteVMScheduleCmd.execute();
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +95,9 @@ public class DeleteVMScheduleCmdTest {
|
|||
*/
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testInvalidParameterValueException() {
|
||||
Mockito.when(vmScheduleManager.removeSchedule(deleteVMScheduleCmd)).thenThrow(InvalidParameterValueException.class);
|
||||
Mockito.when(resourceScheduleManager.removeSchedule(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()
|
||||
)).thenThrow(new InvalidParameterValueException("Invalid schedule"));
|
||||
deleteVMScheduleCmd.execute();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ package org.apache.cloudstack.api.command.user.vm;
|
|||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
|
@ -36,7 +37,8 @@ import java.util.Collections;
|
|||
|
||||
public class ListVMScheduleCmdTest {
|
||||
@Mock
|
||||
public VMScheduleManager vmScheduleManager;
|
||||
public ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@InjectMocks
|
||||
private ListVMScheduleCmd listVMScheduleCmd = new ListVMScheduleCmd();
|
||||
private AutoCloseable closeable;
|
||||
|
|
@ -58,12 +60,14 @@ public class ListVMScheduleCmdTest {
|
|||
*/
|
||||
@Test
|
||||
public void testEmptyResponse() {
|
||||
ListResponse<VMScheduleResponse> response = new ListResponse<VMScheduleResponse>();
|
||||
response.setResponses(new ArrayList<VMScheduleResponse>());
|
||||
Mockito.when(vmScheduleManager.listSchedule(listVMScheduleCmd)).thenReturn(response);
|
||||
ListResponse<ResourceScheduleResponse> response = new ListResponse<ResourceScheduleResponse>();
|
||||
response.setResponses(new ArrayList<ResourceScheduleResponse>());
|
||||
Mockito.when(resourceScheduleManager.listSchedule(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()
|
||||
)).thenReturn(response);
|
||||
listVMScheduleCmd.execute();
|
||||
ListResponse<VMScheduleResponse> actualResponseObject = (ListResponse<VMScheduleResponse>) listVMScheduleCmd.getResponseObject();
|
||||
Assert.assertEquals(response, actualResponseObject);
|
||||
Assert.assertEquals(0L, actualResponseObject.getResponses().size());
|
||||
}
|
||||
|
||||
|
|
@ -74,15 +78,22 @@ public class ListVMScheduleCmdTest {
|
|||
*/
|
||||
@Test
|
||||
public void testNonEmptyResponse() {
|
||||
ListResponse<VMScheduleResponse> listResponse = new ListResponse<VMScheduleResponse>();
|
||||
VMScheduleResponse response = Mockito.mock(VMScheduleResponse.class);
|
||||
ListResponse<ResourceScheduleResponse> listResponse = new ListResponse<ResourceScheduleResponse>();
|
||||
ResourceScheduleResponse response = new ResourceScheduleResponse();
|
||||
response.setId("schedule-uuid");
|
||||
response.setResourceId("vm-uuid");
|
||||
listResponse.setResponses(Collections.singletonList(response));
|
||||
Mockito.when(vmScheduleManager.listSchedule(listVMScheduleCmd)).thenReturn(listResponse);
|
||||
Mockito.when(resourceScheduleManager.listSchedule(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()
|
||||
)).thenReturn(listResponse);
|
||||
listVMScheduleCmd.execute();
|
||||
ListResponse<VMScheduleResponse> actualResponseObject = (ListResponse<VMScheduleResponse>) listVMScheduleCmd.getResponseObject();
|
||||
Assert.assertEquals(listResponse, actualResponseObject);
|
||||
Assert.assertEquals(1L, actualResponseObject.getResponses().size());
|
||||
Assert.assertEquals(response, actualResponseObject.getResponses().get(0));
|
||||
Assert.assertEquals("schedule-uuid",
|
||||
org.springframework.test.util.ReflectionTestUtils.getField(actualResponseObject.getResponses().get(0), "id"));
|
||||
Assert.assertEquals("vm-uuid",
|
||||
org.springframework.test.util.ReflectionTestUtils.getField(actualResponseObject.getResponses().get(0), "vmId"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -92,7 +103,10 @@ public class ListVMScheduleCmdTest {
|
|||
*/
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testInvalidParameterValueException() {
|
||||
Mockito.when(vmScheduleManager.listSchedule(listVMScheduleCmd)).thenThrow(InvalidParameterValueException.class);
|
||||
Mockito.when(resourceScheduleManager.listSchedule(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()
|
||||
)).thenThrow(InvalidParameterValueException.class);
|
||||
listVMScheduleCmd.execute();
|
||||
ListResponse<VMScheduleResponse> actualResponseObject = (ListResponse<VMScheduleResponse>) listVMScheduleCmd.getResponseObject();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,11 @@ package org.apache.cloudstack.api.command.user.vm;
|
|||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
|
||||
import org.apache.cloudstack.schedule.ResourceSchedule;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
|
@ -35,9 +37,11 @@ import org.mockito.MockitoAnnotations;
|
|||
|
||||
public class UpdateVMScheduleCmdTest {
|
||||
@Mock
|
||||
public VMScheduleManager vmScheduleManager;
|
||||
public ResourceScheduleManager resourceScheduleManager;
|
||||
|
||||
@Mock
|
||||
public EntityManager entityManager;
|
||||
|
||||
@InjectMocks
|
||||
private UpdateVMScheduleCmd updateVMScheduleCmd = new UpdateVMScheduleCmd();
|
||||
|
||||
|
|
@ -60,10 +64,18 @@ public class UpdateVMScheduleCmdTest {
|
|||
*/
|
||||
@Test
|
||||
public void testSuccessfulExecution() {
|
||||
VMScheduleResponse vmScheduleResponse = Mockito.mock(VMScheduleResponse.class);
|
||||
Mockito.when(vmScheduleManager.updateSchedule(updateVMScheduleCmd)).thenReturn(vmScheduleResponse);
|
||||
ResourceScheduleResponse scheduleResponse = new ResourceScheduleResponse();
|
||||
scheduleResponse.setId("schedule-uuid");
|
||||
scheduleResponse.setResourceId("vm-uuid");
|
||||
Mockito.when(resourceScheduleManager.updateSchedule(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()
|
||||
)).thenReturn(scheduleResponse);
|
||||
updateVMScheduleCmd.execute();
|
||||
Assert.assertEquals(vmScheduleResponse, updateVMScheduleCmd.getResponseObject());
|
||||
VMScheduleResponse response = (VMScheduleResponse) updateVMScheduleCmd.getResponseObject();
|
||||
Assert.assertNotNull(response);
|
||||
Assert.assertEquals("schedule-uuid", org.springframework.test.util.ReflectionTestUtils.getField(response, "id"));
|
||||
Assert.assertEquals("vm-uuid", org.springframework.test.util.ReflectionTestUtils.getField(response, "vmId"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -73,7 +85,10 @@ public class UpdateVMScheduleCmdTest {
|
|||
*/
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testInvalidParameterValueException() {
|
||||
Mockito.when(vmScheduleManager.updateSchedule(updateVMScheduleCmd)).thenThrow(InvalidParameterValueException.class);
|
||||
Mockito.when(resourceScheduleManager.updateSchedule(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()
|
||||
)).thenThrow(new InvalidParameterValueException("Invalid schedule"));
|
||||
updateVMScheduleCmd.execute();
|
||||
}
|
||||
|
||||
|
|
@ -84,11 +99,12 @@ public class UpdateVMScheduleCmdTest {
|
|||
*/
|
||||
@Test
|
||||
public void testSuccessfulGetEntityOwnerId() {
|
||||
VMSchedule vmSchedule = Mockito.mock(VMSchedule.class);
|
||||
ResourceSchedule schedule = Mockito.mock(ResourceSchedule.class);
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
|
||||
Mockito.when(entityManager.findById(VMSchedule.class, updateVMScheduleCmd.getId())).thenReturn(vmSchedule);
|
||||
Mockito.when(entityManager.findById(VirtualMachine.class, vmSchedule.getVmId())).thenReturn(vm);
|
||||
Mockito.when(schedule.getResourceType()).thenReturn(ApiCommandResourceType.VirtualMachine);
|
||||
Mockito.when(entityManager.findById(ResourceSchedule.class, updateVMScheduleCmd.getId())).thenReturn(schedule);
|
||||
Mockito.when(entityManager.findById(VirtualMachine.class, schedule.getResourceId())).thenReturn(vm);
|
||||
|
||||
long ownerId = updateVMScheduleCmd.getEntityOwnerId();
|
||||
Assert.assertEquals(vm.getAccountId(), ownerId);
|
||||
|
|
@ -101,8 +117,7 @@ public class UpdateVMScheduleCmdTest {
|
|||
*/
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testFailureGetEntityOwnerId() {
|
||||
VMSchedule vmSchedule = Mockito.mock(VMSchedule.class);
|
||||
Mockito.when(entityManager.findById(VMSchedule.class, updateVMScheduleCmd.getId())).thenReturn(null);
|
||||
long ownerId = updateVMScheduleCmd.getEntityOwnerId();
|
||||
Mockito.when(entityManager.findById(ResourceSchedule.class, updateVMScheduleCmd.getId())).thenReturn(null);
|
||||
updateVMScheduleCmd.getEntityOwnerId();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.schedule;
|
||||
|
||||
import org.apache.cloudstack.api.ResourceDetail;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "resource_schedule_details")
|
||||
public class ResourceScheduleDetailVO implements ResourceDetail {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private long id;
|
||||
|
||||
@Column(name = "schedule_id")
|
||||
private long resourceId;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@Column(name = "value", length = 1024)
|
||||
private String value;
|
||||
|
||||
@Column(name = "display")
|
||||
private boolean display = true;
|
||||
|
||||
public ResourceScheduleDetailVO() {
|
||||
}
|
||||
|
||||
public ResourceScheduleDetailVO(long scheduleId, String name, String value, boolean display) {
|
||||
this.resourceId = scheduleId;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.display = display;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisplay() {
|
||||
return display;
|
||||
}
|
||||
}
|
||||
|
|
@ -16,9 +16,10 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule;
|
||||
package org.apache.cloudstack.schedule;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
|
||||
import javax.persistence.Column;
|
||||
|
|
@ -37,8 +38,8 @@ import java.util.TimeZone;
|
|||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "vm_schedule")
|
||||
public class VMScheduleVO implements VMSchedule {
|
||||
@Table(name = "resource_schedule")
|
||||
public class ResourceScheduleVO implements ResourceSchedule {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id", nullable = false)
|
||||
|
|
@ -50,8 +51,12 @@ public class VMScheduleVO implements VMSchedule {
|
|||
@Column(name = "description")
|
||||
String description;
|
||||
|
||||
@Column(name = "vm_id", nullable = false)
|
||||
long vmId;
|
||||
@Enumerated(value = EnumType.STRING)
|
||||
@Column(name = "resource_type", nullable = false)
|
||||
ApiCommandResourceType resourceType;
|
||||
|
||||
@Column(name = "resource_id", nullable = false)
|
||||
long resourceId;
|
||||
|
||||
@Column(name = "schedule", nullable = false)
|
||||
String schedule;
|
||||
|
|
@ -60,8 +65,7 @@ public class VMScheduleVO implements VMSchedule {
|
|||
String timeZone;
|
||||
|
||||
@Column(name = "action", nullable = false)
|
||||
@Enumerated(value = EnumType.STRING)
|
||||
Action action;
|
||||
String actionName;
|
||||
|
||||
@Column(name = "enabled", nullable = false)
|
||||
boolean enabled;
|
||||
|
|
@ -80,17 +84,19 @@ public class VMScheduleVO implements VMSchedule {
|
|||
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||
Date removed;
|
||||
|
||||
public VMScheduleVO() {
|
||||
public ResourceScheduleVO() {
|
||||
uuid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public VMScheduleVO(long vmId, String description, String schedule, String timeZone, Action action, Date startDate, Date endDate, boolean enabled) {
|
||||
public ResourceScheduleVO(ApiCommandResourceType resourceType, long resourceId, String description, String schedule,
|
||||
String timeZone, String action, Date startDate, Date endDate, boolean enabled) {
|
||||
uuid = UUID.randomUUID().toString();
|
||||
this.vmId = vmId;
|
||||
this.resourceType = resourceType;
|
||||
this.resourceId = resourceId;
|
||||
this.description = description;
|
||||
this.schedule = schedule;
|
||||
this.timeZone = timeZone;
|
||||
this.action = action;
|
||||
this.actionName = action;
|
||||
this.startDate = startDate;
|
||||
this.endDate = endDate;
|
||||
this.enabled = enabled;
|
||||
|
|
@ -98,7 +104,8 @@ public class VMScheduleVO implements VMSchedule {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("VMSchedule %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "action", "description"));
|
||||
return String.format("ResourceSchedule %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
|
||||
this, "id", "uuid", "resourceType", "actionName", "description"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -111,14 +118,25 @@ public class VMScheduleVO implements VMSchedule {
|
|||
return id;
|
||||
}
|
||||
|
||||
public long getVmId() {
|
||||
return vmId;
|
||||
@Override
|
||||
public ApiCommandResourceType getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setVmId(long vmId) {
|
||||
this.vmId = vmId;
|
||||
public void setResourceType(ApiCommandResourceType resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(long resourceId) {
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
|
@ -127,6 +145,7 @@ public class VMScheduleVO implements VMSchedule {
|
|||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSchedule() {
|
||||
return schedule.substring(2);
|
||||
}
|
||||
|
|
@ -144,14 +163,16 @@ public class VMScheduleVO implements VMSchedule {
|
|||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
public Action getAction() {
|
||||
return action;
|
||||
@Override
|
||||
public String getActionName() {
|
||||
return actionName;
|
||||
}
|
||||
|
||||
public void setAction(Action action) {
|
||||
this.action = action;
|
||||
public void setActionName(String action) {
|
||||
this.actionName = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
|
@ -183,6 +204,7 @@ public class VMScheduleVO implements VMSchedule {
|
|||
return TimeZone.getTimeZone(getTimeZone()).toZoneId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
|
@ -16,8 +16,9 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule;
|
||||
package org.apache.cloudstack.schedule;
|
||||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
|
||||
import javax.persistence.Column;
|
||||
|
|
@ -34,8 +35,8 @@ import java.util.Date;
|
|||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
@Table(name = "vm_scheduled_job")
|
||||
public class VMScheduledJobVO implements VMScheduledJob {
|
||||
@Table(name = "resource_scheduled_job")
|
||||
public class ResourceScheduledJobVO implements ResourceScheduledJob {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
|
|
@ -44,41 +45,44 @@ public class VMScheduledJobVO implements VMScheduledJob {
|
|||
@Column(name = "uuid", nullable = false)
|
||||
String uuid;
|
||||
|
||||
@Column(name = "vm_id", nullable = false)
|
||||
long vmId;
|
||||
@Enumerated(value = EnumType.STRING)
|
||||
@Column(name = "resource_type", nullable = false)
|
||||
ApiCommandResourceType resourceType;
|
||||
|
||||
@Column(name = "vm_schedule_id", nullable = false)
|
||||
long vmScheduleId;
|
||||
@Column(name = "resource_id", nullable = false)
|
||||
long resourceId;
|
||||
|
||||
@Column(name = "schedule_id", nullable = false)
|
||||
long scheduleId;
|
||||
|
||||
@Column(name = "async_job_id")
|
||||
Long asyncJobId;
|
||||
|
||||
@Column(name = "action", nullable = false)
|
||||
@Enumerated(value = EnumType.STRING)
|
||||
VMSchedule.Action action;
|
||||
String actionName;
|
||||
|
||||
@Column(name = "scheduled_timestamp")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
Date scheduledTime;
|
||||
|
||||
public VMScheduledJobVO() {
|
||||
public ResourceScheduledJobVO() {
|
||||
uuid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public VMScheduledJobVO(long vmId, long vmScheduleId, VMSchedule.Action action, Date scheduledTime) {
|
||||
public ResourceScheduledJobVO(ApiCommandResourceType resourceType, long resourceId, long scheduleId, String action, Date scheduledTime) {
|
||||
uuid = UUID.randomUUID().toString();
|
||||
this.vmId = vmId;
|
||||
this.vmScheduleId = vmScheduleId;
|
||||
this.action = action;
|
||||
this.resourceType = resourceType;
|
||||
this.resourceId = resourceId;
|
||||
this.scheduleId = scheduleId;
|
||||
this.actionName = action;
|
||||
this.scheduledTime = scheduledTime;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("VMScheduledJob %s",
|
||||
return String.format("ResourceScheduledJob %s",
|
||||
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
|
||||
this, "id", "uuid", "action", "vmScheduleId", "vmId", "asyncJobId"));
|
||||
this, "id", "uuid", "resourceType", "actionName", "scheduleId", "resourceId", "asyncJobId"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -92,13 +96,18 @@ public class VMScheduledJobVO implements VMScheduledJob {
|
|||
}
|
||||
|
||||
@Override
|
||||
public long getVmId() {
|
||||
return vmId;
|
||||
public ApiCommandResourceType getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVmScheduleId() {
|
||||
return vmScheduleId;
|
||||
public long getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getScheduleId() {
|
||||
return scheduleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -112,8 +121,12 @@ public class VMScheduledJobVO implements VMScheduledJob {
|
|||
}
|
||||
|
||||
@Override
|
||||
public VMSchedule.Action getAction() {
|
||||
return action;
|
||||
public String getActionName() {
|
||||
return actionName;
|
||||
}
|
||||
|
||||
public void setActionName(String action) {
|
||||
this.actionName = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -16,22 +16,25 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule.dao;
|
||||
package org.apache.cloudstack.schedule.dao;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduleVO;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface VMScheduleDao extends GenericDao<VMScheduleVO, Long> {
|
||||
List<VMScheduleVO> listAllActiveSchedules();
|
||||
public interface ResourceScheduleDao extends GenericDao<ResourceScheduleVO, Long> {
|
||||
List<ResourceScheduleVO> listAllActiveSchedules(ApiCommandResourceType resourceType);
|
||||
|
||||
long removeSchedulesForVmIdAndIds(Long vmId, List<Long> ids);
|
||||
long removeSchedulesForResourceAndIds(ApiCommandResourceType resourceType, long resourceId, List<Long> ids);
|
||||
|
||||
Pair<List<VMScheduleVO>, Integer> searchAndCount(Long id, Long vmId, VMSchedule.Action action, Boolean enabled, Long offset, Long limit);
|
||||
long removeAllSchedulesForResource(ApiCommandResourceType resourceType, long resourceId);
|
||||
|
||||
SearchCriteria<VMScheduleVO> getSearchCriteriaForVMId(Long vmId);
|
||||
Pair<List<ResourceScheduleVO>, Integer> searchAndCount(List<Long> ids, ApiCommandResourceType resourceType, Long resourceId,
|
||||
String action, Boolean enabled, Long offset, Long limit);
|
||||
|
||||
SearchCriteria<ResourceScheduleVO> getSearchCriteriaForResource(ApiCommandResourceType resourceType, long resourceId);
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.schedule.dao;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleVO;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class ResourceScheduleDaoImpl extends GenericDaoBase<ResourceScheduleVO, Long> implements ResourceScheduleDao {
|
||||
|
||||
private final SearchBuilder<ResourceScheduleVO> activeScheduleSearch;
|
||||
private final SearchBuilder<ResourceScheduleVO> allSearch;
|
||||
|
||||
static final String RESOURCE_TYPE = "resourceType";
|
||||
static final String RESOURCE_ID = "resourceId";
|
||||
|
||||
public ResourceScheduleDaoImpl() {
|
||||
super();
|
||||
|
||||
activeScheduleSearch = createSearchBuilder();
|
||||
activeScheduleSearch.and(RESOURCE_TYPE, activeScheduleSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
||||
activeScheduleSearch.and(ApiConstants.ENABLED, activeScheduleSearch.entity().getEnabled(), SearchCriteria.Op.EQ);
|
||||
activeScheduleSearch.and().op(activeScheduleSearch.entity().getEndDate(), SearchCriteria.Op.NULL);
|
||||
activeScheduleSearch.or(ApiConstants.END_DATE, activeScheduleSearch.entity().getEndDate(), SearchCriteria.Op.GT);
|
||||
activeScheduleSearch.cp();
|
||||
activeScheduleSearch.done();
|
||||
|
||||
allSearch = createSearchBuilder();
|
||||
allSearch.and(ApiConstants.ID, allSearch.entity().getId(), SearchCriteria.Op.IN);
|
||||
allSearch.and(RESOURCE_TYPE, allSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
||||
allSearch.and(RESOURCE_ID, allSearch.entity().getResourceId(), SearchCriteria.Op.EQ);
|
||||
allSearch.and(ApiConstants.ACTION, allSearch.entity().getActionName(), SearchCriteria.Op.EQ);
|
||||
allSearch.and(ApiConstants.ENABLED, allSearch.entity().getEnabled(), SearchCriteria.Op.EQ);
|
||||
allSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourceScheduleVO> listAllActiveSchedules(ApiCommandResourceType resourceType) {
|
||||
SearchCriteria<ResourceScheduleVO> sc = activeScheduleSearch.create();
|
||||
sc.setParameters(RESOURCE_TYPE, resourceType);
|
||||
sc.setParameters(ApiConstants.ENABLED, true);
|
||||
sc.setParameters(ApiConstants.END_DATE, new Date());
|
||||
return search(sc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long removeSchedulesForResourceAndIds(ApiCommandResourceType resourceType, long resourceId, List<Long> ids) {
|
||||
SearchCriteria<ResourceScheduleVO> sc = allSearch.create();
|
||||
if (CollectionUtils.isNotEmpty(ids)) {
|
||||
sc.setParameters(ApiConstants.ID, ids.toArray());
|
||||
}
|
||||
sc.setParameters(RESOURCE_TYPE, resourceType);
|
||||
sc.setParameters(RESOURCE_ID, resourceId);
|
||||
return remove(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long removeAllSchedulesForResource(ApiCommandResourceType resourceType, long resourceId) {
|
||||
SearchCriteria<ResourceScheduleVO> sc = allSearch.create();
|
||||
sc.setParameters(RESOURCE_TYPE, resourceType);
|
||||
sc.setParameters(RESOURCE_ID, resourceId);
|
||||
return remove(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<ResourceScheduleVO>, Integer> searchAndCount(List<Long> ids, ApiCommandResourceType resourceType, Long resourceId,
|
||||
String action, Boolean enabled, Long offset, Long limit) {
|
||||
SearchCriteria<ResourceScheduleVO> sc = allSearch.create();
|
||||
if (CollectionUtils.isNotEmpty(ids)) {
|
||||
sc.setParameters(ApiConstants.ID, ids.toArray());
|
||||
}
|
||||
sc.setParametersIfNotNull(ApiConstants.ENABLED, enabled);
|
||||
sc.setParametersIfNotNull(ApiConstants.ACTION, action);
|
||||
sc.setParametersIfNotNull(RESOURCE_TYPE, resourceType);
|
||||
sc.setParametersIfNotNull(RESOURCE_ID, resourceId);
|
||||
Filter filter = new Filter(ResourceScheduleVO.class, ApiConstants.ID, false, offset, limit);
|
||||
return searchAndCount(sc, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchCriteria<ResourceScheduleVO> getSearchCriteriaForResource(ApiCommandResourceType resourceType, long resourceId) {
|
||||
SearchCriteria<ResourceScheduleVO> sc = allSearch.create();
|
||||
sc.setParameters(RESOURCE_TYPE, resourceType);
|
||||
sc.setParameters(RESOURCE_ID, resourceId);
|
||||
return sc;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.schedule.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.resourcedetail.ResourceDetailsDao;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleDetailVO;
|
||||
|
||||
public interface ResourceScheduleDetailsDao extends GenericDao<ResourceScheduleDetailVO, Long>, ResourceDetailsDao<ResourceScheduleDetailVO> {
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.schedule.dao;
|
||||
|
||||
import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleDetailVO;
|
||||
|
||||
public class ResourceScheduleDetailsDaoImpl extends ResourceDetailsDaoBase<ResourceScheduleDetailVO> implements ResourceScheduleDetailsDao {
|
||||
|
||||
@Override
|
||||
public void addDetail(long resourceId, String key, String value, boolean display) {
|
||||
super.addDetail(new ResourceScheduleDetailVO(resourceId, key, value, display));
|
||||
}
|
||||
}
|
||||
|
|
@ -16,21 +16,21 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule.dao;
|
||||
package org.apache.cloudstack.schedule.dao;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduledJobVO;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduledJobVO;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public interface VMScheduledJobDao extends GenericDao<VMScheduledJobVO, Long> {
|
||||
public interface ResourceScheduledJobDao extends GenericDao<ResourceScheduledJobVO, Long> {
|
||||
List<ResourceScheduledJobVO> listJobsToStart(ApiCommandResourceType resourceType, Date currentTimestamp);
|
||||
|
||||
List<VMScheduledJobVO> listJobsToStart(Date currentTimestamp);
|
||||
int expungeJobsForSchedules(List<Long> scheduleIds, Date dateAfter);
|
||||
|
||||
int expungeJobsForSchedules(List<Long> scheduleId, Date dateAfter);
|
||||
int expungeJobsBefore(ApiCommandResourceType resourceType, Date currentTimestamp);
|
||||
|
||||
int expungeJobsBefore(Date currentTimestamp);
|
||||
|
||||
VMScheduledJobVO findByScheduleAndTimestamp(long scheduleId, Date scheduledTimestamp);
|
||||
ResourceScheduledJobVO findByScheduleAndTimestamp(long scheduleId, Date scheduledTimestamp);
|
||||
}
|
||||
|
|
@ -16,13 +16,14 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule.dao;
|
||||
package org.apache.cloudstack.schedule.dao;
|
||||
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduledJobVO;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduledJobVO;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
|
@ -31,62 +32,59 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class VMScheduledJobDaoImpl extends GenericDaoBase<VMScheduledJobVO, Long> implements VMScheduledJobDao {
|
||||
public class ResourceScheduledJobDaoImpl extends GenericDaoBase<ResourceScheduledJobVO, Long> implements ResourceScheduledJobDao {
|
||||
|
||||
private final SearchBuilder<VMScheduledJobVO> jobsToStartSearch;
|
||||
private final SearchBuilder<ResourceScheduledJobVO> jobsToStartSearch;
|
||||
private final SearchBuilder<ResourceScheduledJobVO> expungeJobsBeforeSearch;
|
||||
private final SearchBuilder<ResourceScheduledJobVO> expungeJobForScheduleSearch;
|
||||
private final SearchBuilder<ResourceScheduledJobVO> scheduleAndTimestampSearch;
|
||||
|
||||
private final SearchBuilder<VMScheduledJobVO> expungeJobsBeforeSearch;
|
||||
static final String SCHEDULED_TIMESTAMP = "scheduledTimestamp";
|
||||
static final String SCHEDULE_ID = "scheduleId";
|
||||
static final String RESOURCE_TYPE = "resourceType";
|
||||
|
||||
private final SearchBuilder<VMScheduledJobVO> expungeJobForScheduleSearch;
|
||||
|
||||
private final SearchBuilder<VMScheduledJobVO> scheduleAndTimestampSearch;
|
||||
|
||||
static final String SCHEDULED_TIMESTAMP = "scheduled_timestamp";
|
||||
|
||||
static final String VM_SCHEDULE_ID = "vm_schedule_id";
|
||||
|
||||
public VMScheduledJobDaoImpl() {
|
||||
public ResourceScheduledJobDaoImpl() {
|
||||
super();
|
||||
|
||||
jobsToStartSearch = createSearchBuilder();
|
||||
jobsToStartSearch.and(RESOURCE_TYPE, jobsToStartSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
||||
jobsToStartSearch.and(SCHEDULED_TIMESTAMP, jobsToStartSearch.entity().getScheduledTime(), SearchCriteria.Op.EQ);
|
||||
jobsToStartSearch.and("async_job_id", jobsToStartSearch.entity().getAsyncJobId(), SearchCriteria.Op.NULL);
|
||||
jobsToStartSearch.done();
|
||||
|
||||
expungeJobsBeforeSearch = createSearchBuilder();
|
||||
expungeJobsBeforeSearch.and(RESOURCE_TYPE, expungeJobsBeforeSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
||||
expungeJobsBeforeSearch.and(SCHEDULED_TIMESTAMP, expungeJobsBeforeSearch.entity().getScheduledTime(), SearchCriteria.Op.LT);
|
||||
expungeJobsBeforeSearch.done();
|
||||
|
||||
expungeJobForScheduleSearch = createSearchBuilder();
|
||||
expungeJobForScheduleSearch.and(VM_SCHEDULE_ID, expungeJobForScheduleSearch.entity().getVmScheduleId(), SearchCriteria.Op.IN);
|
||||
expungeJobForScheduleSearch.and(SCHEDULE_ID, expungeJobForScheduleSearch.entity().getScheduleId(), SearchCriteria.Op.IN);
|
||||
expungeJobForScheduleSearch.and(SCHEDULED_TIMESTAMP, expungeJobForScheduleSearch.entity().getScheduledTime(), SearchCriteria.Op.GTEQ);
|
||||
expungeJobForScheduleSearch.done();
|
||||
|
||||
scheduleAndTimestampSearch = createSearchBuilder();
|
||||
scheduleAndTimestampSearch.and(VM_SCHEDULE_ID, scheduleAndTimestampSearch.entity().getVmScheduleId(), SearchCriteria.Op.EQ);
|
||||
scheduleAndTimestampSearch.and(SCHEDULE_ID, scheduleAndTimestampSearch.entity().getScheduleId(), SearchCriteria.Op.EQ);
|
||||
scheduleAndTimestampSearch.and(SCHEDULED_TIMESTAMP, scheduleAndTimestampSearch.entity().getScheduledTime(), SearchCriteria.Op.EQ);
|
||||
scheduleAndTimestampSearch.done();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution of job wouldn't be at exact seconds. So, we round off and then execute.
|
||||
*/
|
||||
@Override
|
||||
public List<VMScheduledJobVO> listJobsToStart(Date currentTimestamp) {
|
||||
public List<ResourceScheduledJobVO> listJobsToStart(ApiCommandResourceType resourceType, Date currentTimestamp) {
|
||||
if (currentTimestamp == null) {
|
||||
currentTimestamp = new Date();
|
||||
}
|
||||
Date truncatedTs = DateUtils.round(currentTimestamp, Calendar.MINUTE);
|
||||
|
||||
SearchCriteria<VMScheduledJobVO> sc = jobsToStartSearch.create();
|
||||
SearchCriteria<ResourceScheduledJobVO> sc = jobsToStartSearch.create();
|
||||
sc.setParameters(RESOURCE_TYPE, resourceType);
|
||||
sc.setParameters(SCHEDULED_TIMESTAMP, truncatedTs);
|
||||
Filter filter = new Filter(VMScheduledJobVO.class, "vmScheduleId", true, null, null);
|
||||
Filter filter = new Filter(ResourceScheduledJobVO.class, "scheduleId", true, null, null);
|
||||
return search(sc, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expungeJobsForSchedules(List<Long> vmScheduleIds, Date dateAfter) {
|
||||
SearchCriteria<VMScheduledJobVO> sc = expungeJobForScheduleSearch.create();
|
||||
sc.setParameters(VM_SCHEDULE_ID, vmScheduleIds.toArray());
|
||||
public int expungeJobsForSchedules(List<Long> scheduleIds, Date dateAfter) {
|
||||
SearchCriteria<ResourceScheduledJobVO> sc = expungeJobForScheduleSearch.create();
|
||||
sc.setParameters(SCHEDULE_ID, scheduleIds.toArray());
|
||||
if (dateAfter != null) {
|
||||
sc.setParameters(SCHEDULED_TIMESTAMP, dateAfter);
|
||||
}
|
||||
|
|
@ -94,16 +92,17 @@ public class VMScheduledJobDaoImpl extends GenericDaoBase<VMScheduledJobVO, Long
|
|||
}
|
||||
|
||||
@Override
|
||||
public int expungeJobsBefore(Date date) {
|
||||
SearchCriteria<VMScheduledJobVO> sc = expungeJobsBeforeSearch.create();
|
||||
public int expungeJobsBefore(ApiCommandResourceType resourceType, Date date) {
|
||||
SearchCriteria<ResourceScheduledJobVO> sc = expungeJobsBeforeSearch.create();
|
||||
sc.setParameters(RESOURCE_TYPE, resourceType);
|
||||
sc.setParameters(SCHEDULED_TIMESTAMP, date);
|
||||
return expunge(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMScheduledJobVO findByScheduleAndTimestamp(long scheduleId, Date scheduledTimestamp) {
|
||||
SearchCriteria<VMScheduledJobVO> sc = scheduleAndTimestampSearch.create();
|
||||
sc.setParameters(VM_SCHEDULE_ID, scheduleId);
|
||||
public ResourceScheduledJobVO findByScheduleAndTimestamp(long scheduleId, Date scheduledTimestamp) {
|
||||
SearchCriteria<ResourceScheduledJobVO> sc = scheduleAndTimestampSearch.create();
|
||||
sc.setParameters(SCHEDULE_ID, scheduleId);
|
||||
sc.setParameters(SCHEDULED_TIMESTAMP, scheduledTimestamp);
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule.dao;
|
||||
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.vm.schedule.VMSchedule;
|
||||
import org.apache.cloudstack.vm.schedule.VMScheduleVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class VMScheduleDaoImpl extends GenericDaoBase<VMScheduleVO, Long> implements VMScheduleDao {
|
||||
|
||||
private final SearchBuilder<VMScheduleVO> activeScheduleSearch;
|
||||
|
||||
private final SearchBuilder<VMScheduleVO> scheduleSearchByVmIdAndIds;
|
||||
|
||||
private final SearchBuilder<VMScheduleVO> scheduleSearch;
|
||||
|
||||
public VMScheduleDaoImpl() {
|
||||
super();
|
||||
activeScheduleSearch = createSearchBuilder();
|
||||
activeScheduleSearch.and(ApiConstants.ENABLED, activeScheduleSearch.entity().getEnabled(), SearchCriteria.Op.EQ);
|
||||
activeScheduleSearch.and().op(activeScheduleSearch.entity().getEndDate(), SearchCriteria.Op.NULL);
|
||||
activeScheduleSearch.or(ApiConstants.END_DATE, activeScheduleSearch.entity().getEndDate(), SearchCriteria.Op.GT);
|
||||
activeScheduleSearch.cp();
|
||||
activeScheduleSearch.done();
|
||||
|
||||
scheduleSearchByVmIdAndIds = createSearchBuilder();
|
||||
scheduleSearchByVmIdAndIds.and(ApiConstants.ID, scheduleSearchByVmIdAndIds.entity().getId(), SearchCriteria.Op.IN);
|
||||
scheduleSearchByVmIdAndIds.and(ApiConstants.VIRTUAL_MACHINE_ID, scheduleSearchByVmIdAndIds.entity().getVmId(), SearchCriteria.Op.EQ);
|
||||
scheduleSearchByVmIdAndIds.done();
|
||||
|
||||
scheduleSearch = createSearchBuilder();
|
||||
scheduleSearch.and(ApiConstants.ID, scheduleSearch.entity().getId(), SearchCriteria.Op.EQ);
|
||||
scheduleSearch.and(ApiConstants.VIRTUAL_MACHINE_ID, scheduleSearch.entity().getVmId(), SearchCriteria.Op.EQ);
|
||||
scheduleSearch.and(ApiConstants.ACTION, scheduleSearch.entity().getAction(), SearchCriteria.Op.EQ);
|
||||
scheduleSearch.and(ApiConstants.ENABLED, scheduleSearch.entity().getEnabled(), SearchCriteria.Op.EQ);
|
||||
scheduleSearch.done();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VMScheduleVO> listAllActiveSchedules() {
|
||||
// WHERE enabled = true AND (end_date IS NULL OR end_date > current_date)
|
||||
SearchCriteria<VMScheduleVO> sc = activeScheduleSearch.create();
|
||||
sc.setParameters(ApiConstants.ENABLED, true);
|
||||
sc.setParameters(ApiConstants.END_DATE, new Date());
|
||||
return search(sc, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long removeSchedulesForVmIdAndIds(Long vmId, List<Long> ids) {
|
||||
SearchCriteria<VMScheduleVO> sc = scheduleSearchByVmIdAndIds.create();
|
||||
sc.setParameters(ApiConstants.ID, ids.toArray());
|
||||
sc.setParameters(ApiConstants.VIRTUAL_MACHINE_ID, vmId);
|
||||
return remove(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<List<VMScheduleVO>, Integer> searchAndCount(Long id, Long vmId, VMSchedule.Action action, Boolean enabled, Long offset, Long limit) {
|
||||
SearchCriteria<VMScheduleVO> sc = scheduleSearch.create();
|
||||
|
||||
if (id != null) {
|
||||
sc.setParameters(ApiConstants.ID, id);
|
||||
}
|
||||
if (enabled != null) {
|
||||
sc.setParameters(ApiConstants.ENABLED, enabled);
|
||||
}
|
||||
if (action != null) {
|
||||
sc.setParameters(ApiConstants.ACTION, action);
|
||||
}
|
||||
sc.setParameters(ApiConstants.VIRTUAL_MACHINE_ID, vmId);
|
||||
|
||||
Filter filter = new Filter(VMScheduleVO.class, ApiConstants.ID, false, offset, limit);
|
||||
return searchAndCount(sc, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchCriteria<VMScheduleVO> getSearchCriteriaForVMId(Long vmId) {
|
||||
SearchCriteria<VMScheduleVO> sc = scheduleSearch.create();
|
||||
sc.setParameters(ApiConstants.VIRTUAL_MACHINE_ID, vmId);
|
||||
return sc;
|
||||
}
|
||||
}
|
||||
|
|
@ -282,8 +282,9 @@
|
|||
<bean id="secondaryStorageHeuristicDaoImpl" class="org.apache.cloudstack.secstorage.dao.SecondaryStorageHeuristicDaoImpl" />
|
||||
<bean id="heuristicRuleHelper" class="org.apache.cloudstack.storage.heuristics.HeuristicRuleHelper" />
|
||||
<bean id="publicIpQuarantineDaoImpl" class="com.cloud.network.dao.PublicIpQuarantineDaoImpl" />
|
||||
<bean id="VMScheduleDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduleDaoImpl" />
|
||||
<bean id="VMScheduledJobDaoImpl" class="org.apache.cloudstack.vm.schedule.dao.VMScheduledJobDaoImpl" />
|
||||
<bean id="ResourceScheduleDaoImpl" class="org.apache.cloudstack.schedule.dao.ResourceScheduleDaoImpl" />
|
||||
<bean id="ResourceScheduleDetailsDaoImpl" class="org.apache.cloudstack.schedule.dao.ResourceScheduleDetailsDaoImpl" />
|
||||
<bean id="ResourceScheduledJobDaoImpl" class="org.apache.cloudstack.schedule.dao.ResourceScheduledJobDaoImpl" />
|
||||
<bean id="VmwareDatacenterDaoImpl" class="com.cloud.hypervisor.vmware.dao.VmwareDatacenterDaoImpl" />
|
||||
<bean id="vnfTemplateDetailsDaoImpl" class="com.cloud.storage.dao.VnfTemplateDetailsDaoImpl" />
|
||||
<bean id="vnfTemplateNicDaoImpl" class="com.cloud.storage.dao.VnfTemplateNicDaoImpl" />
|
||||
|
|
|
|||
|
|
@ -131,3 +131,58 @@ CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_tariff_usage` (
|
|||
-- Add the 'keep_mac_address_on_public_nic' column to the 'cloud.networks' and 'cloud.vpc' tables
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.networks', 'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc', 'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1');
|
||||
|
||||
-- Generalise VM schedule tables into resource-agnostic resource_schedule / resource_scheduled_job.
|
||||
-- Step 1: rename vm_schedule → resource_schedule, rename vm_id → resource_id, add resource_type column.
|
||||
ALTER TABLE `cloud`.`vm_schedule`
|
||||
DROP FOREIGN KEY `fk_vm_schedule__vm_id`,
|
||||
DROP INDEX `i_vm_schedule__vm_id`,
|
||||
DROP INDEX `i_vm_schedule__enabled_end_date`,
|
||||
CHANGE COLUMN `vm_id` `resource_id` bigint unsigned NOT NULL COMMENT 'id of the scheduled resource',
|
||||
ADD COLUMN `resource_type` varchar(64) NOT NULL DEFAULT 'VirtualMachine' COMMENT 'type of the scheduled resource' AFTER `uuid`;
|
||||
|
||||
RENAME TABLE `cloud`.`vm_schedule` TO `cloud`.`resource_schedule`;
|
||||
|
||||
ALTER TABLE `cloud`.`resource_schedule`
|
||||
ADD INDEX `i_resource_schedule__resource` (`resource_type`, `resource_id`),
|
||||
ADD INDEX `i_resource_schedule__enabled_end_date` (`enabled`, `end_date`);
|
||||
|
||||
-- Step 2: rename vm_scheduled_job → resource_scheduled_job, rename columns.
|
||||
ALTER TABLE `cloud`.`vm_scheduled_job`
|
||||
DROP FOREIGN KEY `fk_vm_scheduled_job__vm_id`,
|
||||
DROP FOREIGN KEY `fk_vm_scheduled_job__vm_schedule_id`,
|
||||
DROP INDEX `i_vm_scheduled_job__vm_id`,
|
||||
DROP INDEX `i_vm_scheduled_job__scheduled_timestamp`,
|
||||
DROP INDEX `vm_schedule_id`,
|
||||
CHANGE COLUMN `vm_id` `resource_id` bigint unsigned NOT NULL COMMENT 'id of the scheduled resource',
|
||||
CHANGE COLUMN `vm_schedule_id` `schedule_id` bigint unsigned NOT NULL COMMENT 'id of the resource_schedule row',
|
||||
ADD COLUMN `resource_type` varchar(64) NOT NULL DEFAULT 'VirtualMachine' COMMENT 'type of the scheduled resource' AFTER `uuid`;
|
||||
|
||||
RENAME TABLE `cloud`.`vm_scheduled_job` TO `cloud`.`resource_scheduled_job`;
|
||||
|
||||
ALTER TABLE `cloud`.`resource_scheduled_job`
|
||||
ADD UNIQUE KEY `uc_resource_scheduled_job__schedule_timestamp` (`schedule_id`, `scheduled_timestamp`),
|
||||
ADD INDEX `i_resource_scheduled_job__resource` (`resource_type`, `resource_id`),
|
||||
ADD INDEX `i_resource_scheduled_job__scheduled_timestamp` (`scheduled_timestamp`),
|
||||
ADD CONSTRAINT `fk_resource_scheduled_job__schedule_id` FOREIGN KEY (`schedule_id`) REFERENCES `resource_schedule`(`id`) ON DELETE CASCADE;
|
||||
|
||||
-- Step 3: details table for action-specific parameters (used by the generic resource schedule API in Commit 2).
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`resource_schedule_details` (
|
||||
`id` bigint unsigned NOT NULL auto_increment,
|
||||
`schedule_id` bigint unsigned NOT NULL COMMENT 'id of the resource_schedule row',
|
||||
`name` varchar(255) NOT NULL,
|
||||
`value` varchar(1024) NOT NULL,
|
||||
`display` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'should this detail be visible to the end user',
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `i_resource_schedule_details__schedule_id` (`schedule_id`),
|
||||
CONSTRAINT `fk_resource_schedule_details__schedule_id` FOREIGN KEY (`schedule_id`) REFERENCES `resource_schedule`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Step 4: rename CRUD event types from VM.SCHEDULE.{CREATE,UPDATE,DELETE} to the new generic SCHEDULE.{CREATE,UPDATE,DELETE}.
|
||||
-- Action-execution events (VM.SCHEDULE.START, .STOP, .REBOOT, .FORCE_STOP, .FORCE_REBOOT) keep their existing names.
|
||||
UPDATE `cloud`.`event` SET `type` = 'SCHEDULE.CREATE' WHERE `type` = 'VM.SCHEDULE.CREATE';
|
||||
UPDATE `cloud`.`event` SET `type` = 'SCHEDULE.UPDATE' WHERE `type` = 'VM.SCHEDULE.UPDATE';
|
||||
UPDATE `cloud`.`event` SET `type` = 'SCHEDULE.DELETE' WHERE `type` = 'VM.SCHEDULE.DELETE';
|
||||
|
||||
-- Step 5: Rename the global configuration key for the scheduler
|
||||
UPDATE `cloud`.`configuration` SET name='scheduler.jobs.expire.interval' WHERE name='vmscheduler.jobs.expire.interval';
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ import org.apache.cloudstack.backup.BackupScheduleVO;
|
|||
import org.apache.cloudstack.backup.BackupVO;
|
||||
import org.apache.cloudstack.backup.dao.BackupDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupScheduleDao;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleManager;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity;
|
||||
import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMNetworkMapDao;
|
||||
|
|
@ -427,6 +428,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
@Inject
|
||||
private EntityManager _entityMgr;
|
||||
@Inject
|
||||
private ResourceScheduleManager resourceScheduleManager;
|
||||
@Inject
|
||||
private HostDao _hostDao;
|
||||
@Inject
|
||||
private ServiceOfferingDao serviceOfferingDao;
|
||||
|
|
@ -2588,6 +2591,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||
|
||||
autoScaleManager.removeVmFromVmGroup(vm.getId());
|
||||
|
||||
resourceScheduleManager.removeSchedulesForResource(ApiCommandResourceType.VirtualMachine, vm.getId());
|
||||
|
||||
releaseNetworkResourcesOnExpunge(vm.getId());
|
||||
|
||||
List<VolumeVO> rootVol = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,393 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.schedule;
|
||||
|
||||
import com.cloud.api.ApiGsonHelper;
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.db.GlobalLock;
|
||||
import com.google.common.primitives.Longs;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
||||
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
|
||||
import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
|
||||
import org.apache.cloudstack.schedule.dao.ResourceScheduleDao;
|
||||
import org.apache.cloudstack.schedule.dao.ResourceScheduledJobDao;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.springframework.scheduling.support.CronExpression;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityExistsException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* Base class for per-resource-type schedule workers.
|
||||
* Each subclass owns a dedicated {@link Timer} and {@link GlobalLock} keyed by
|
||||
* its resource type, so VM scheduling and AutoScale scheduling (for example) run
|
||||
* independently and cannot block each other.
|
||||
*/
|
||||
public abstract class BaseScheduleWorker extends ManagerBase {
|
||||
|
||||
public static final ConfigKey<Integer> ScheduledJobExpireInterval = new ConfigKey<>(
|
||||
ConfigKey.CATEGORY_ADVANCED, Integer.class,
|
||||
"scheduler.jobs.expire.interval", "30",
|
||||
"Scheduled job expiry interval in days (applies to all resource-type schedulers)", true);
|
||||
|
||||
@Inject
|
||||
protected ResourceScheduleDao resourceScheduleDao;
|
||||
|
||||
@Inject
|
||||
protected ResourceScheduledJobDao resourceScheduledJobDao;
|
||||
|
||||
@Inject
|
||||
protected AsyncJobManager asyncJobManager;
|
||||
|
||||
protected AsyncJobDispatcher asyncJobDispatcher;
|
||||
private Timer schedulerTimer;
|
||||
protected Date currentTimestamp;
|
||||
|
||||
public AsyncJobDispatcher getAsyncJobDispatcher() {
|
||||
return asyncJobDispatcher;
|
||||
}
|
||||
|
||||
public void setAsyncJobDispatcher(final AsyncJobDispatcher dispatcher) {
|
||||
asyncJobDispatcher = dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* The API resource type this worker handles (e.g. {@code ApiCommandResourceType.VirtualMachine}).
|
||||
*/
|
||||
public abstract ApiCommandResourceType getApiResourceType();
|
||||
|
||||
/**
|
||||
* Convenience method returning {@code getApiResourceType().name()} for use in DAO queries, locks, and logging.
|
||||
*/
|
||||
protected final String getResourceTypeName() {
|
||||
return getApiResourceType().name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action described by {@code job} against the owning resource.
|
||||
*
|
||||
* @return the async-job id, or {@code null} if the job was skipped.
|
||||
*/
|
||||
protected abstract Long processJob(ResourceScheduledJobVO job);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Lifecycle
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
currentTimestamp = DateUtils.addMinutes(new Date(), 1);
|
||||
scheduleNextJobs(currentTimestamp);
|
||||
|
||||
final TimerTask pollTask = new ManagedContextTimerTask() {
|
||||
@Override
|
||||
protected void runInContext() {
|
||||
try {
|
||||
poll(new Date());
|
||||
} catch (final Throwable t) {
|
||||
logger.warn("Uncaught throwable in {} scheduler", getResourceTypeName(), t);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
schedulerTimer = new Timer(getResourceTypeName() + "SchedulerPollTask");
|
||||
schedulerTimer.scheduleAtFixedRate(pollTask, 5000L, 60 * 1000L);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stop() {
|
||||
if (schedulerTimer != null) {
|
||||
schedulerTimer.cancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Poll loop (identical structure for every resource type)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public void poll(Date timestamp) {
|
||||
currentTimestamp = DateUtils.round(timestamp, Calendar.MINUTE);
|
||||
String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp);
|
||||
logger.debug("{} scheduler poll at {}", getResourceTypeName(), displayTime);
|
||||
|
||||
GlobalLock scanLock = GlobalLock.getInternLock("resourceScheduler.poll." + getResourceTypeName());
|
||||
try {
|
||||
if (scanLock.lock(30)) {
|
||||
try {
|
||||
scheduleNextJobs(currentTimestamp);
|
||||
} finally {
|
||||
scanLock.unlock();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
scanLock.releaseRef();
|
||||
}
|
||||
|
||||
scanLock = GlobalLock.getInternLock("resourceScheduler.poll." + getResourceTypeName());
|
||||
try {
|
||||
if (scanLock.lock(30)) {
|
||||
try {
|
||||
startJobs();
|
||||
} finally {
|
||||
scanLock.unlock();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
scanLock.releaseRef();
|
||||
}
|
||||
|
||||
try {
|
||||
cleanupScheduledJobs();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Error cleaning up scheduled jobs for {}", getResourceTypeName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Scheduling helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void scheduleNextJobs(Date timestamp) {
|
||||
for (ResourceScheduleVO schedule : resourceScheduleDao.listAllActiveSchedules(getApiResourceType())) {
|
||||
try {
|
||||
scheduleNextJob(schedule, timestamp);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Error scheduling next job for schedule {}", schedule, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Date scheduleNextJob(ResourceScheduleVO schedule, Date timestamp) {
|
||||
if (!schedule.getEnabled()) {
|
||||
logger.debug("Schedule {} is disabled. Skipping.", schedule);
|
||||
return null;
|
||||
}
|
||||
|
||||
CronExpression cron = DateUtil.parseSchedule(schedule.getSchedule());
|
||||
Date startDate = schedule.getStartDate();
|
||||
Date endDate = schedule.getEndDate();
|
||||
|
||||
if (!isResourceValid(schedule.getResourceId())) {
|
||||
logger.info("Resource id={} is no longer valid. Disabling schedule {}.", schedule.getResourceId(), schedule);
|
||||
schedule.setEnabled(false);
|
||||
resourceScheduleDao.persist(schedule);
|
||||
return null;
|
||||
}
|
||||
|
||||
ZonedDateTime now = (timestamp != null)
|
||||
? ZonedDateTime.ofInstant(timestamp.toInstant(), schedule.getTimeZoneId())
|
||||
: ZonedDateTime.now(schedule.getTimeZoneId());
|
||||
ZonedDateTime zonedStart = ZonedDateTime.ofInstant(startDate.toInstant(), schedule.getTimeZoneId());
|
||||
ZonedDateTime zonedEnd = (endDate != null)
|
||||
? ZonedDateTime.ofInstant(endDate.toInstant(), schedule.getTimeZoneId())
|
||||
: null;
|
||||
|
||||
if (zonedEnd != null && now.isAfter(zonedEnd)) {
|
||||
logger.info("End time has passed. Disabling schedule {}.", schedule);
|
||||
schedule.setEnabled(false);
|
||||
resourceScheduleDao.persist(schedule);
|
||||
return null;
|
||||
}
|
||||
|
||||
ZonedDateTime ts = zonedStart.isAfter(now) ? cron.next(zonedStart) : cron.next(now);
|
||||
if (ts == null) {
|
||||
logger.info("No next schedule time found. Disabling schedule {}.", schedule);
|
||||
schedule.setEnabled(false);
|
||||
resourceScheduleDao.persist(schedule);
|
||||
return null;
|
||||
}
|
||||
|
||||
Date scheduledDateTime = Date.from(ts.toInstant());
|
||||
ResourceScheduledJobVO existingJob = resourceScheduledJobDao.findByScheduleAndTimestamp(schedule.getId(), scheduledDateTime);
|
||||
if (existingJob != null) {
|
||||
logger.trace("Job already scheduled for {} at {}", schedule, scheduledDateTime);
|
||||
return scheduledDateTime;
|
||||
}
|
||||
|
||||
ResourceScheduledJobVO job = new ResourceScheduledJobVO(
|
||||
getApiResourceType(), schedule.getResourceId(), schedule.getId(),
|
||||
schedule.getActionName(), scheduledDateTime);
|
||||
try {
|
||||
resourceScheduledJobDao.persist(job);
|
||||
long accountId = getEntityOwnerId(schedule.getResourceId());
|
||||
ActionEventUtils.onScheduledActionEvent(
|
||||
User.UID_SYSTEM, accountId,
|
||||
parseAction(schedule.getActionName()).getEventType(),
|
||||
String.format("Scheduled action (%s) [resource: %d, schedule: %s] at %s",
|
||||
schedule.getActionName(), schedule.getResourceId(), schedule, scheduledDateTime),
|
||||
schedule.getResourceId(), getResourceTypeName(), true, 0);
|
||||
} catch (EntityExistsException e) {
|
||||
logger.debug("Job already scheduled (concurrent insert).");
|
||||
}
|
||||
return scheduledDateTime;
|
||||
}
|
||||
|
||||
public void updateScheduledJob(ResourceScheduleVO schedule) {
|
||||
removeScheduledJobs(Longs.asList(schedule.getId()));
|
||||
scheduleNextJob(schedule, new Date());
|
||||
}
|
||||
|
||||
public void removeScheduledJobs(List<Long> scheduleIds) {
|
||||
if (CollectionUtils.isEmpty(scheduleIds)) {
|
||||
return;
|
||||
}
|
||||
int removed = resourceScheduledJobDao.expungeJobsForSchedules(scheduleIds, new Date());
|
||||
logger.debug("Removed {} scheduled jobs for schedules {}", removed, scheduleIds);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Job execution
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void startJobs() {
|
||||
String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp);
|
||||
List<ResourceScheduledJobVO> jobs = resourceScheduledJobDao.listJobsToStart(getApiResourceType(), currentTimestamp);
|
||||
logger.debug("Got {} scheduled jobs for {} at {}", jobs.size(), getResourceTypeName(), displayTime);
|
||||
|
||||
Map<Long, ResourceScheduledJobVO> toExecute = new HashMap<>();
|
||||
Map<Long, List<ResourceScheduledJobVO>> toSkip = new HashMap<>();
|
||||
|
||||
for (ResourceScheduledJobVO job : jobs) {
|
||||
long resourceId = job.getResourceId();
|
||||
if (toExecute.get(resourceId) == null) {
|
||||
toExecute.put(resourceId, job);
|
||||
} else {
|
||||
toSkip.computeIfAbsent(resourceId, k -> new ArrayList<>()).add(job);
|
||||
}
|
||||
}
|
||||
|
||||
executeJobs(toExecute);
|
||||
logSkippedJobs(toExecute, toSkip);
|
||||
}
|
||||
|
||||
public void executeJobs(Map<Long, ResourceScheduledJobVO> jobsToExecute) {
|
||||
for (Map.Entry<Long, ResourceScheduledJobVO> entry : jobsToExecute.entrySet()) {
|
||||
ResourceScheduledJobVO job = entry.getValue();
|
||||
ResourceScheduledJobVO locked = null;
|
||||
try {
|
||||
locked = resourceScheduledJobDao.acquireInLockTable(job.getId());
|
||||
Long jobId = processJob(job);
|
||||
if (jobId != null) {
|
||||
locked.setAsyncJobId(jobId);
|
||||
resourceScheduledJobDao.update(job.getId(), locked);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed executing scheduled job {}", job, e);
|
||||
} finally {
|
||||
if (locked != null) {
|
||||
resourceScheduledJobDao.releaseFromLockTable(job.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logSkippedJobs(Map<Long, ResourceScheduledJobVO> executed,
|
||||
Map<Long, List<ResourceScheduledJobVO>> skipped) {
|
||||
for (Map.Entry<Long, List<ResourceScheduledJobVO>> entry : skipped.entrySet()) {
|
||||
long resourceId = entry.getKey();
|
||||
ResourceScheduledJobVO running = executed.get(resourceId);
|
||||
for (ResourceScheduledJobVO s : entry.getValue()) {
|
||||
logger.info("Skipping job {} for resource {} — conflict with {}", s, resourceId, running);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupScheduledJobs() {
|
||||
Date deleteBeforeDate = DateUtils.addDays(currentTimestamp,
|
||||
-1 * ScheduledJobExpireInterval.value());
|
||||
int removed = resourceScheduledJobDao.expungeJobsBefore(getApiResourceType(), deleteBeforeDate);
|
||||
logger.info("Cleaned up {} scheduled job entries for {}", removed, getResourceTypeName());
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Subclass helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public abstract boolean isResourceValid(long resourceId);
|
||||
|
||||
public abstract long getEntityOwnerId(long resourceId);
|
||||
|
||||
/**
|
||||
* Parses an action string into the resource-type-specific typed action constant. Throws InvalidParameterValueException for unknown values.
|
||||
*/
|
||||
public abstract ResourceSchedule.Action parseAction(String action);
|
||||
|
||||
/**
|
||||
* Validates action-specific detail parameters. Throws InvalidParameterValueException on failure.
|
||||
*/
|
||||
public abstract void validateDetails(ResourceSchedule.Action action, Map<String, String> details);
|
||||
|
||||
/**
|
||||
* Submits an async job for the given command class.
|
||||
*
|
||||
* @param cmdClass the command to dispatch
|
||||
* @param accountId account submitting the job
|
||||
* @param resourceId primary resource id (written to AsyncJobVO.instanceId)
|
||||
* @param eventId the start-event id for correlation
|
||||
* @param extra additional parameters (e.g. "forced" -> "true")
|
||||
* @return the async job id
|
||||
*/
|
||||
public <T extends BaseCmd> long submitAsyncJob(
|
||||
Class<T> cmdClass, long accountId, long resourceId, long eventId,
|
||||
Map<String, String> extra) {
|
||||
Map<String, String> params = new HashMap<>(extra);
|
||||
params.put(ApiConstants.ID, String.valueOf(resourceId));
|
||||
params.put("ctxUserId", "1");
|
||||
params.put("ctxAccountId", String.valueOf(accountId));
|
||||
params.put(ApiConstants.CTX_START_EVENT_ID, String.valueOf(eventId));
|
||||
|
||||
T cmd;
|
||||
try {
|
||||
cmd = cmdClass.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to instantiate " + cmdClass.getName(), e);
|
||||
}
|
||||
ComponentContext.inject(cmd);
|
||||
|
||||
AsyncJobVO job = new AsyncJobVO("", User.UID_SYSTEM, accountId,
|
||||
cmdClass.getName(),
|
||||
ApiGsonHelper.getBuilder().create().toJson(params),
|
||||
resourceId,
|
||||
cmd.getApiResourceType() != null ? cmd.getApiResourceType().toString() : null,
|
||||
null);
|
||||
job.setDispatcher(asyncJobDispatcher.getName());
|
||||
return asyncJobManager.submitAsyncJob(job);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,444 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package org.apache.cloudstack.schedule;
|
||||
|
||||
import com.cloud.api.query.MutualExclusiveIdsManagerBase;
|
||||
import com.cloud.event.ActionEvent;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.component.PluggableService;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallback;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
import org.apache.cloudstack.api.command.user.schedule.CreateResourceScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.schedule.DeleteResourceScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.schedule.ListResourceScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.schedule.UpdateResourceScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.CreateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.DeleteVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.ListVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.UpdateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.schedule.dao.ResourceScheduleDao;
|
||||
import org.apache.cloudstack.schedule.dao.ResourceScheduleDetailsDao;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.scheduling.support.CronExpression;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ResourceScheduleManagerImpl extends MutualExclusiveIdsManagerBase implements ResourceScheduleManager, PluggableService, Configurable {
|
||||
|
||||
@Inject
|
||||
private ResourceScheduleDao resourceScheduleDao;
|
||||
|
||||
@Inject
|
||||
private ResourceScheduleDetailsDao resourceScheduleDetailsDao;
|
||||
|
||||
@Inject
|
||||
private AccountManager accountManager;
|
||||
|
||||
@Inject
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Inject
|
||||
private List<BaseScheduleWorker> workerList;
|
||||
|
||||
private Map<ApiCommandResourceType, BaseScheduleWorker> workerMap;
|
||||
|
||||
@Override
|
||||
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||
workerMap = new HashMap<>();
|
||||
if (workerList != null) {
|
||||
for (BaseScheduleWorker worker : workerList) {
|
||||
workerMap.put(worker.getApiResourceType(), worker);
|
||||
}
|
||||
}
|
||||
return super.configure(name, params);
|
||||
}
|
||||
|
||||
private BaseScheduleWorker getWorker(ApiCommandResourceType resourceType) {
|
||||
BaseScheduleWorker worker = workerMap.get(resourceType);
|
||||
if (worker == null) {
|
||||
throw new InvalidParameterValueException("Scheduling is not supported for resource type: " + resourceType);
|
||||
}
|
||||
return worker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getCommands() {
|
||||
final List<Class<?>> cmdList = new ArrayList<>();
|
||||
cmdList.add(CreateVMScheduleCmd.class);
|
||||
cmdList.add(ListVMScheduleCmd.class);
|
||||
cmdList.add(UpdateVMScheduleCmd.class);
|
||||
cmdList.add(DeleteVMScheduleCmd.class);
|
||||
cmdList.add(CreateResourceScheduleCmd.class);
|
||||
cmdList.add(ListResourceScheduleCmd.class);
|
||||
cmdList.add(UpdateResourceScheduleCmd.class);
|
||||
cmdList.add(DeleteResourceScheduleCmd.class);
|
||||
return cmdList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigComponentName() {
|
||||
return ResourceScheduleManager.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey<?>[]{
|
||||
BaseScheduleWorker.ScheduledJobExpireInterval
|
||||
};
|
||||
}
|
||||
|
||||
// Helper to resolve UUID string to internal ID
|
||||
private long resolveResourceId(String resourceIdStr, Class<?> entityClass) {
|
||||
if (entityClass == null) {
|
||||
throw new CloudRuntimeException("Entity class is required to resolve resource ID");
|
||||
}
|
||||
Object obj = entityManager.findByUuid(entityClass, resourceIdStr);
|
||||
if (obj == null) {
|
||||
try {
|
||||
long id = Long.parseLong(resourceIdStr);
|
||||
obj = entityManager.findById(entityClass, id);
|
||||
if (obj == null) {
|
||||
throw new InvalidParameterValueException("Unable to find resource by id " + resourceIdStr);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InvalidParameterValueException("Unable to find resource by id " + resourceIdStr);
|
||||
}
|
||||
}
|
||||
return ((InternalIdentity) obj).getId();
|
||||
}
|
||||
|
||||
private String getResourceUuid(long internalId, Class<?> entityClass) {
|
||||
if (entityClass != null) {
|
||||
Object obj = entityManager.findById(entityClass, internalId);
|
||||
if (obj instanceof Identity) {
|
||||
return ((Identity) obj).getUuid();
|
||||
}
|
||||
}
|
||||
return String.valueOf(internalId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_SCHEDULE_CREATE, eventDescription = "Creating Resource Schedule", create = true)
|
||||
public ResourceScheduleResponse createSchedule(ApiCommandResourceType resourceType, String resourceUuid, String description,
|
||||
String schedule, String timeZoneStr, String action,
|
||||
Date cmdStartDate, Date cmdEndDate, boolean enabled,
|
||||
Map<String, String> details) {
|
||||
BaseScheduleWorker worker = getWorker(resourceType);
|
||||
|
||||
long internalResourceId = resolveResourceId(resourceUuid, worker.getApiResourceType().getAssociatedClass());
|
||||
|
||||
if (!worker.isResourceValid(internalResourceId)) {
|
||||
throw new InvalidParameterValueException("Invalid or non-existent resource: " + resourceUuid);
|
||||
}
|
||||
|
||||
long ownerId = worker.getEntityOwnerId(internalResourceId);
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, accountManager.getAccount(ownerId));
|
||||
|
||||
ResourceSchedule.Action parsedAction = worker.parseAction(action);
|
||||
|
||||
worker.validateDetails(parsedAction, details);
|
||||
|
||||
TimeZone timeZone = TimeZone.getTimeZone(timeZoneStr);
|
||||
String timeZoneId = timeZone.getID();
|
||||
Date startDate = DateUtils.addMinutes(new Date(), 1);
|
||||
if (cmdStartDate != null) {
|
||||
startDate = Date.from(DateUtil.getZoneDateTime(cmdStartDate, timeZone.toZoneId()).toInstant());
|
||||
}
|
||||
Date endDate = null;
|
||||
if (cmdEndDate != null) {
|
||||
endDate = Date.from(DateUtil.getZoneDateTime(cmdEndDate, timeZone.toZoneId()).toInstant());
|
||||
}
|
||||
|
||||
CronExpression cronExpression = DateUtil.parseSchedule(schedule);
|
||||
validateStartDateEndDate(startDate, endDate, timeZone);
|
||||
|
||||
if (StringUtils.isBlank(description)) {
|
||||
description = String.format("%s - %s", parsedAction.name(), DateUtil.getHumanReadableSchedule(cronExpression));
|
||||
}
|
||||
|
||||
logger.warn("Using timezone [{}] for running the schedule for resource [{}], as an equivalent of [{}].", timeZoneId, resourceUuid, timeZoneStr);
|
||||
|
||||
String finalDescription = description;
|
||||
String finalAction = parsedAction.name();
|
||||
Date finalStartDate = startDate;
|
||||
Date finalEndDate = endDate;
|
||||
|
||||
return Transaction.execute((TransactionCallback<ResourceScheduleResponse>) status -> {
|
||||
ResourceScheduleVO scheduleVO = resourceScheduleDao.persist(new ResourceScheduleVO(
|
||||
resourceType, internalResourceId,
|
||||
finalDescription, cronExpression.toString(), timeZoneId,
|
||||
finalAction, finalStartDate, finalEndDate, enabled));
|
||||
|
||||
if (details != null && !details.isEmpty()) {
|
||||
List<ResourceScheduleDetailVO> detailVOs = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : details.entrySet()) {
|
||||
detailVOs.add(new ResourceScheduleDetailVO(scheduleVO.getId(), entry.getKey(), entry.getValue(), true));
|
||||
}
|
||||
resourceScheduleDetailsDao.saveDetails(detailVOs);
|
||||
}
|
||||
|
||||
worker.scheduleNextJob(scheduleVO, new Date());
|
||||
|
||||
CallContext.current().setEventResourceId(internalResourceId);
|
||||
CallContext.current().setEventResourceType(worker.getApiResourceType());
|
||||
return createResponse(scheduleVO, details);
|
||||
});
|
||||
}
|
||||
|
||||
ResourceScheduleResponse createResponse(ResourceSchedule schedule, Map<String, String> details) {
|
||||
if (details == null || details.isEmpty()) {
|
||||
details = resourceScheduleDetailsDao.listDetailsKeyPairs(schedule.getId(), true);
|
||||
}
|
||||
|
||||
BaseScheduleWorker worker = getWorker(schedule.getResourceType());
|
||||
|
||||
ResourceScheduleResponse response = new ResourceScheduleResponse();
|
||||
response.setObjectName("resourceschedule");
|
||||
response.setId(schedule.getUuid());
|
||||
response.setResourceType(schedule.getResourceType());
|
||||
|
||||
String uuid = getResourceUuid(schedule.getResourceId(), worker.getApiResourceType().getAssociatedClass());
|
||||
response.setResourceId(uuid);
|
||||
|
||||
response.setDescription(schedule.getDescription());
|
||||
response.setSchedule(schedule.getSchedule());
|
||||
response.setTimeZone(schedule.getTimeZone());
|
||||
response.setAction(worker.parseAction(schedule.getActionName()));
|
||||
response.setEnabled(schedule.getEnabled());
|
||||
response.setStartDate(schedule.getStartDate());
|
||||
response.setEndDate(schedule.getEndDate());
|
||||
response.setDetails(details);
|
||||
response.setCreated(schedule.getCreated());
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListResponse<ResourceScheduleResponse> listSchedule(Long id, List<Long> ids, ApiCommandResourceType resourceType,
|
||||
String resourceUuid, String action, Boolean enabled,
|
||||
Long startIndex, Long pageSize) {
|
||||
Long internalResourceId = null;
|
||||
BaseScheduleWorker worker = getWorker(resourceType);
|
||||
if (StringUtils.isBlank(resourceUuid)) {
|
||||
throw new InvalidParameterValueException("Resource ID must be specified");
|
||||
} else {
|
||||
internalResourceId = resolveResourceId(resourceUuid, worker.getApiResourceType().getAssociatedClass());
|
||||
long ownerId = worker.getEntityOwnerId(internalResourceId);
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, accountManager.getAccount(ownerId));
|
||||
}
|
||||
|
||||
List<Long> scheduleIds = getIdsListFromCmd(id, ids);
|
||||
if (action != null) {
|
||||
action = worker.parseAction(action).name();
|
||||
}
|
||||
|
||||
Pair<List<ResourceScheduleVO>, Integer> result = resourceScheduleDao.searchAndCount(
|
||||
scheduleIds, resourceType, internalResourceId, action, enabled, startIndex, pageSize);
|
||||
|
||||
ListResponse<ResourceScheduleResponse> response = new ListResponse<>();
|
||||
List<ResourceScheduleResponse> responsesList = new ArrayList<>();
|
||||
for (ResourceScheduleVO schedule : result.first()) {
|
||||
responsesList.add(createResponse(schedule, null));
|
||||
}
|
||||
response.setResponses(responsesList, result.second());
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_SCHEDULE_UPDATE, eventDescription = "Updating Resource Schedule")
|
||||
public ResourceScheduleResponse updateSchedule(Long id, String description, String schedule,
|
||||
String timeZoneStr, Date cmdStartDate, Date cmdEndDate,
|
||||
Boolean enabled, Map<String, String> details) {
|
||||
ResourceScheduleVO scheduleVO = resourceScheduleDao.findById(id);
|
||||
|
||||
if (scheduleVO == null) {
|
||||
throw new CloudRuntimeException("Resource schedule doesn't exist");
|
||||
}
|
||||
|
||||
BaseScheduleWorker worker = getWorker(scheduleVO.getResourceType());
|
||||
long ownerId = worker.getEntityOwnerId(scheduleVO.getResourceId());
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, accountManager.getAccount(ownerId));
|
||||
|
||||
if (details != null && !details.isEmpty()) {
|
||||
worker.validateDetails(worker.parseAction(scheduleVO.getActionName()), details);
|
||||
}
|
||||
|
||||
CronExpression cronExpression = Objects.requireNonNullElse(
|
||||
DateUtil.parseSchedule(schedule),
|
||||
DateUtil.parseSchedule(scheduleVO.getSchedule())
|
||||
);
|
||||
|
||||
if (description == null && scheduleVO.getDescription() == null) {
|
||||
description = String.format("%s - %s", scheduleVO.getActionName(), DateUtil.getHumanReadableSchedule(cronExpression));
|
||||
}
|
||||
|
||||
final String originalTimeZone = scheduleVO.getTimeZone();
|
||||
final Date originalStartDate = scheduleVO.getStartDate();
|
||||
final Date originalEndDate = scheduleVO.getEndDate();
|
||||
|
||||
TimeZone timeZone;
|
||||
String timeZoneId;
|
||||
if (timeZoneStr != null) {
|
||||
timeZone = TimeZone.getTimeZone(timeZoneStr);
|
||||
timeZoneId = timeZone.getID();
|
||||
if (!timeZoneId.equals(timeZoneStr)) {
|
||||
logger.warn("Using timezone [{}] for running the schedule [{}] for resource {}, as an equivalent of [{}].",
|
||||
timeZoneId, scheduleVO.getSchedule(), scheduleVO.getResourceId(), timeZoneStr);
|
||||
}
|
||||
scheduleVO.setTimeZone(timeZoneId);
|
||||
} else {
|
||||
timeZoneId = scheduleVO.getTimeZone();
|
||||
timeZone = TimeZone.getTimeZone(timeZoneId);
|
||||
}
|
||||
|
||||
Date startDate = scheduleVO.getStartDate().before(DateUtils.addMinutes(new Date(), 1)) ? DateUtils.addMinutes(new Date(), 1) : scheduleVO.getStartDate();
|
||||
Date endDate = scheduleVO.getEndDate();
|
||||
if (cmdEndDate != null) {
|
||||
endDate = Date.from(DateUtil.getZoneDateTime(cmdEndDate, timeZone.toZoneId()).toInstant());
|
||||
}
|
||||
|
||||
if (cmdStartDate != null) {
|
||||
startDate = Date.from(DateUtil.getZoneDateTime(cmdStartDate, timeZone.toZoneId()).toInstant());
|
||||
}
|
||||
|
||||
if (ObjectUtils.anyNotNull(cmdStartDate, cmdEndDate, timeZoneStr) &&
|
||||
(!Objects.equals(originalTimeZone, timeZoneId) ||
|
||||
!Objects.equals(originalStartDate, startDate) ||
|
||||
!Objects.equals(originalEndDate, endDate))) {
|
||||
validateStartDateEndDate(Objects.requireNonNullElse(startDate, DateUtils.addMinutes(new Date(), 1)),
|
||||
endDate, timeZone);
|
||||
}
|
||||
|
||||
if (enabled != null) {
|
||||
scheduleVO.setEnabled(enabled);
|
||||
}
|
||||
if (description != null) {
|
||||
scheduleVO.setDescription(description);
|
||||
}
|
||||
if (cmdEndDate != null) {
|
||||
scheduleVO.setEndDate(endDate);
|
||||
}
|
||||
if (cmdStartDate != null) {
|
||||
scheduleVO.setStartDate(startDate);
|
||||
}
|
||||
scheduleVO.setSchedule(cronExpression.toString());
|
||||
|
||||
return Transaction.execute((TransactionCallback<ResourceScheduleResponse>) status -> {
|
||||
resourceScheduleDao.update(id, scheduleVO);
|
||||
|
||||
if (details != null) {
|
||||
if (details.isEmpty()) {
|
||||
resourceScheduleDetailsDao.removeDetails(id);
|
||||
} else {
|
||||
List<ResourceScheduleDetailVO> detailVOs = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : details.entrySet()) {
|
||||
detailVOs.add(new ResourceScheduleDetailVO(id, entry.getKey(), entry.getValue(), true));
|
||||
}
|
||||
resourceScheduleDetailsDao.saveDetails(detailVOs);
|
||||
}
|
||||
}
|
||||
|
||||
worker.updateScheduledJob(scheduleVO);
|
||||
|
||||
CallContext.current().setEventResourceId(scheduleVO.getResourceId());
|
||||
CallContext.current().setEventResourceType(worker.getApiResourceType());
|
||||
|
||||
// Re-load details if they weren't fully replaced
|
||||
Map<String, String> currentDetails = resourceScheduleDetailsDao.listDetailsKeyPairs(id, true);
|
||||
return createResponse(scheduleVO, currentDetails);
|
||||
});
|
||||
}
|
||||
|
||||
void validateStartDateEndDate(Date startDate, Date endDate, TimeZone tz) {
|
||||
ZonedDateTime now = ZonedDateTime.now(tz.toZoneId());
|
||||
ZonedDateTime zonedStartDate = ZonedDateTime.ofInstant(startDate.toInstant(), tz.toZoneId());
|
||||
|
||||
if (zonedStartDate.isBefore(now)) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid value for start date. Start date [%s] can't be before current time [%s].", zonedStartDate, now));
|
||||
}
|
||||
|
||||
if (endDate != null) {
|
||||
ZonedDateTime zonedEndDate = ZonedDateTime.ofInstant(endDate.toInstant(), tz.toZoneId());
|
||||
if (zonedEndDate.isBefore(now)) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid value for end date. End date [%s] can't be before current time [%s].", zonedEndDate, now));
|
||||
}
|
||||
if (zonedEndDate.isBefore(zonedStartDate)) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid value for end date. End date [%s] can't be before start date [%s].", zonedEndDate, zonedStartDate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSchedulesForResource(ApiCommandResourceType resourceType, long resourceId) {
|
||||
List<ResourceScheduleVO> schedules = resourceScheduleDao.search(
|
||||
resourceScheduleDao.getSearchCriteriaForResource(resourceType, resourceId), null);
|
||||
List<Long> ids = new ArrayList<>();
|
||||
for (ResourceScheduleVO schedule : schedules) {
|
||||
ids.add(schedule.getId());
|
||||
}
|
||||
|
||||
getWorker(resourceType).removeScheduledJobs(ids);
|
||||
resourceScheduleDao.removeAllSchedulesForResource(resourceType, resourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_SCHEDULE_DELETE, eventDescription = "Deleting Resource Schedule")
|
||||
public Long removeSchedule(ApiCommandResourceType resourceType, String resourceUuid, Long id, List<Long> idsList) {
|
||||
BaseScheduleWorker worker = getWorker(resourceType);
|
||||
long internalResourceId = resolveResourceId(resourceUuid, worker.getApiResourceType().getAssociatedClass());
|
||||
|
||||
long ownerId = worker.getEntityOwnerId(internalResourceId);
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, accountManager.getAccount(ownerId));
|
||||
|
||||
List<Long> ids = getIdsListFromCmd(id, idsList);
|
||||
Pair<List<ResourceScheduleVO>, Integer> result = resourceScheduleDao.searchAndCount(ids, resourceType, internalResourceId, null, null, null, null);
|
||||
List<ResourceScheduleVO> schedulesToRemove = result.first();
|
||||
List<Long> scheduleIdsToRemove = schedulesToRemove.stream().map(ResourceScheduleVO::getId).collect(Collectors.toList());
|
||||
return Transaction.execute((TransactionCallback<Long>) status -> {
|
||||
worker.removeScheduledJobs(scheduleIdsToRemove);
|
||||
|
||||
CallContext.current().setEventResourceId(internalResourceId);
|
||||
CallContext.current().setEventResourceType(worker.getApiResourceType());
|
||||
return resourceScheduleDao.removeSchedulesForResourceAndIds(resourceType, internalResourceId, scheduleIdsToRemove);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.schedule.vm;
|
||||
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.StopVMCmd;
|
||||
import org.apache.cloudstack.schedule.BaseScheduleWorker;
|
||||
import org.apache.cloudstack.schedule.ResourceSchedule;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduledJobVO;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class VMScheduleWorker extends BaseScheduleWorker {
|
||||
|
||||
@Inject
|
||||
private UserVmManager userVmManager;
|
||||
|
||||
@Override
|
||||
public ApiCommandResourceType getApiResourceType() {
|
||||
return ApiCommandResourceType.VirtualMachine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceValid(long resourceId) {
|
||||
return userVmManager.getUserVm(resourceId) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId(long resourceId) {
|
||||
VirtualMachine vm = userVmManager.getUserVm(resourceId);
|
||||
return vm != null ? vm.getAccountId() : User.UID_SYSTEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMScheduleAction parseAction(String actionName) {
|
||||
VMScheduleAction action = EnumUtils.getEnumIgnoreCase(VMScheduleAction.class, actionName);
|
||||
if (action == null) {
|
||||
throw new InvalidParameterValueException(String.format(
|
||||
"Invalid action for VirtualMachine schedule: %s. Supported actions: %s",
|
||||
actionName, Arrays.toString(VMScheduleAction.values())));
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateDetails(ResourceSchedule.Action action, Map<String, String> details) {}
|
||||
|
||||
@Override
|
||||
protected Long processJob(ResourceScheduledJobVO job) {
|
||||
VirtualMachine vm = userVmManager.getUserVm(job.getResourceId());
|
||||
if (vm == null) {
|
||||
logger.warn("VM id={} not found; skipping scheduled job {}", job.getResourceId(), job);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Arrays.asList(VirtualMachine.State.Running, VirtualMachine.State.Stopped).contains(vm.getState())) {
|
||||
logger.info("Skipping action ({}) for [vm: {}, job: {}] — VM is in state: {}",
|
||||
job.getActionName(), vm, job, vm.getState());
|
||||
return null;
|
||||
}
|
||||
|
||||
VMScheduleAction action = parseAction(job.getActionName());
|
||||
final long eventId = ActionEventUtils.onCompletedActionEvent(
|
||||
User.UID_SYSTEM, vm.getAccountId(), null,
|
||||
action.getEventType(), true,
|
||||
String.format("Executing action (%s) for VM: %s", action, vm),
|
||||
vm.getId(), ApiCommandResourceType.VirtualMachine.toString(), 0);
|
||||
|
||||
if (vm.getState() == VirtualMachine.State.Running) {
|
||||
switch (action) {
|
||||
case STOP:
|
||||
return submitStopVMJob(vm, false, eventId);
|
||||
case FORCE_STOP:
|
||||
return submitStopVMJob(vm, true, eventId);
|
||||
case REBOOT:
|
||||
return submitRebootVMJob(vm, false, eventId);
|
||||
case FORCE_REBOOT:
|
||||
return submitRebootVMJob(vm, true, eventId);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (vm.getState() == VirtualMachine.State.Stopped && action == VMScheduleAction.START) {
|
||||
return submitStartVMJob(vm, eventId);
|
||||
}
|
||||
|
||||
logger.warn("Skipping action ({}) for [vm: {}, job: {}] — VM is in state: {}",
|
||||
action, vm, job, vm.getState());
|
||||
return null;
|
||||
}
|
||||
|
||||
private long submitStartVMJob(VirtualMachine vm, long eventId) {
|
||||
return submitAsyncJob(StartVMCmd.class, vm.getAccountId(), vm.getId(), eventId, Collections.emptyMap());
|
||||
}
|
||||
|
||||
private long submitStopVMJob(VirtualMachine vm, boolean forced, long eventId) {
|
||||
return submitAsyncJob(StopVMCmd.class, vm.getAccountId(), vm.getId(), eventId,
|
||||
Map.of(ApiConstants.FORCED, String.valueOf(forced)));
|
||||
}
|
||||
|
||||
private long submitRebootVMJob(VirtualMachine vm, boolean forced, long eventId) {
|
||||
return submitAsyncJob(RebootVMCmd.class, vm.getAccountId(), vm.getId(), eventId,
|
||||
Map.of(ApiConstants.FORCED, String.valueOf(forced)));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,326 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.command.user.vm.CreateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.DeleteVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.ListVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.UpdateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.vm.schedule.dao.VMScheduleDao;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.scheduling.support.CronExpression;
|
||||
|
||||
import com.cloud.api.query.MutualExclusiveIdsManagerBase;
|
||||
import com.cloud.event.ActionEvent;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.component.PluggableService;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.utils.db.Transaction;
|
||||
import com.cloud.utils.db.TransactionCallback;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
|
||||
public class VMScheduleManagerImpl extends MutualExclusiveIdsManagerBase implements VMScheduleManager, PluggableService {
|
||||
|
||||
@Inject
|
||||
private VMScheduleDao vmScheduleDao;
|
||||
@Inject
|
||||
private UserVmManager userVmManager;
|
||||
@Inject
|
||||
private VMScheduler vmScheduler;
|
||||
@Inject
|
||||
private AccountManager accountManager;
|
||||
|
||||
@Override
|
||||
public List<Class<?>> getCommands() {
|
||||
final List<Class<?>> cmdList = new ArrayList<>();
|
||||
cmdList.add(CreateVMScheduleCmd.class);
|
||||
cmdList.add(ListVMScheduleCmd.class);
|
||||
cmdList.add(UpdateVMScheduleCmd.class);
|
||||
cmdList.add(DeleteVMScheduleCmd.class);
|
||||
return cmdList;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VM_SCHEDULE_CREATE, eventDescription = "Creating VM Schedule", create = true)
|
||||
public VMScheduleResponse createSchedule(CreateVMScheduleCmd cmd) {
|
||||
VirtualMachine vm = userVmManager.getUserVm(cmd.getVmId());
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, vm);
|
||||
if (vm == null) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid value for vmId: %s", cmd.getVmId()));
|
||||
}
|
||||
|
||||
VMSchedule.Action action = null;
|
||||
if (cmd.getAction() != null) {
|
||||
try {
|
||||
action = VMSchedule.Action.valueOf(cmd.getAction().toUpperCase());
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid value for action: %s", cmd.getAction()));
|
||||
}
|
||||
}
|
||||
|
||||
Date cmdStartDate = cmd.getStartDate();
|
||||
Date cmdEndDate = cmd.getEndDate();
|
||||
String cmdTimeZone = cmd.getTimeZone();
|
||||
TimeZone timeZone = TimeZone.getTimeZone(cmdTimeZone);
|
||||
String timeZoneId = timeZone.getID();
|
||||
Date startDate = DateUtils.addMinutes(new Date(), 1);
|
||||
if (cmdStartDate != null) {
|
||||
startDate = Date.from(DateUtil.getZoneDateTime(cmdStartDate, timeZone.toZoneId()).toInstant());
|
||||
}
|
||||
Date endDate = null;
|
||||
if (cmdEndDate != null) {
|
||||
endDate = Date.from(DateUtil.getZoneDateTime(cmdEndDate, timeZone.toZoneId()).toInstant());
|
||||
}
|
||||
|
||||
CronExpression cronExpression = DateUtil.parseSchedule(cmd.getSchedule());
|
||||
|
||||
validateStartDateEndDate(startDate, endDate, timeZone);
|
||||
|
||||
String description = null;
|
||||
if (StringUtils.isBlank(cmd.getDescription())) {
|
||||
description = String.format("%s - %s", action, DateUtil.getHumanReadableSchedule(cronExpression));
|
||||
} else description = cmd.getDescription();
|
||||
|
||||
logger.warn("Using timezone [{}] for running the schedule for VM [{}], as an equivalent of [{}].", timeZoneId, vm, cmdTimeZone);
|
||||
|
||||
String finalDescription = description;
|
||||
VMSchedule.Action finalAction = action;
|
||||
Date finalStartDate = startDate;
|
||||
Date finalEndDate = endDate;
|
||||
|
||||
return Transaction.execute((TransactionCallback<VMScheduleResponse>) status -> {
|
||||
VMScheduleVO vmSchedule = vmScheduleDao.persist(new VMScheduleVO(cmd.getVmId(), finalDescription, cronExpression.toString(), timeZoneId, finalAction, finalStartDate, finalEndDate, cmd.getEnabled()));
|
||||
vmScheduler.scheduleNextJob(vmSchedule, new Date());
|
||||
CallContext.current().setEventResourceId(vm.getId());
|
||||
CallContext.current().setEventResourceType(ApiCommandResourceType.VirtualMachine);
|
||||
return createResponse(vmSchedule);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMScheduleResponse createResponse(VMSchedule vmSchedule) {
|
||||
VirtualMachine vm = userVmManager.getUserVm(vmSchedule.getVmId());
|
||||
VMScheduleResponse response = new VMScheduleResponse();
|
||||
|
||||
response.setObjectName(VMSchedule.class.getSimpleName().toLowerCase());
|
||||
response.setId(vmSchedule.getUuid());
|
||||
response.setVmId(vm.getUuid());
|
||||
response.setDescription(vmSchedule.getDescription());
|
||||
response.setSchedule(vmSchedule.getSchedule());
|
||||
response.setTimeZone(vmSchedule.getTimeZone());
|
||||
response.setAction(vmSchedule.getAction());
|
||||
response.setEnabled(vmSchedule.getEnabled());
|
||||
response.setStartDate(vmSchedule.getStartDate());
|
||||
response.setEndDate(vmSchedule.getEndDate());
|
||||
response.setCreated(vmSchedule.getCreated());
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListResponse<VMScheduleResponse> listSchedule(ListVMScheduleCmd cmd) {
|
||||
Long id = cmd.getId();
|
||||
Boolean enabled = cmd.getEnabled();
|
||||
Long vmId = cmd.getVmId();
|
||||
|
||||
VirtualMachine vm = userVmManager.getUserVm(vmId);
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, vm);
|
||||
|
||||
VMSchedule.Action action = null;
|
||||
if (cmd.getAction() != null) {
|
||||
try {
|
||||
action = VMSchedule.Action.valueOf(cmd.getAction());
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw new InvalidParameterValueException("Invalid value for action: " + cmd.getAction());
|
||||
}
|
||||
}
|
||||
|
||||
Pair<List<VMScheduleVO>, Integer> result = vmScheduleDao.searchAndCount(id, vmId, action, enabled, cmd.getStartIndex(), cmd.getPageSizeVal());
|
||||
|
||||
ListResponse<VMScheduleResponse> response = new ListResponse<>();
|
||||
List<VMScheduleResponse> responsesList = new ArrayList<>();
|
||||
for (VMSchedule vmSchedule : result.first()) {
|
||||
responsesList.add(createResponse(vmSchedule));
|
||||
}
|
||||
response.setResponses(responsesList, result.second());
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VM_SCHEDULE_UPDATE, eventDescription = "Updating VM Schedule")
|
||||
public VMScheduleResponse updateSchedule(UpdateVMScheduleCmd cmd) {
|
||||
Long id = cmd.getId();
|
||||
VMScheduleVO vmSchedule = vmScheduleDao.findById(id);
|
||||
|
||||
if (vmSchedule == null) {
|
||||
throw new CloudRuntimeException("VM schedule doesn't exist");
|
||||
}
|
||||
|
||||
VirtualMachine vm = userVmManager.getUserVm(vmSchedule.getVmId());
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, vm);
|
||||
|
||||
CronExpression cronExpression = Objects.requireNonNullElse(
|
||||
DateUtil.parseSchedule(cmd.getSchedule()),
|
||||
DateUtil.parseSchedule(vmSchedule.getSchedule())
|
||||
);
|
||||
|
||||
String description = cmd.getDescription();
|
||||
if (description == null && vmSchedule.getDescription() == null) {
|
||||
description = String.format("%s - %s", vmSchedule.getAction(), DateUtil.getHumanReadableSchedule(cronExpression));
|
||||
}
|
||||
String cmdTimeZone = cmd.getTimeZone();
|
||||
Date cmdStartDate = cmd.getStartDate();
|
||||
Date cmdEndDate = cmd.getEndDate();
|
||||
Boolean enabled = cmd.getEnabled();
|
||||
final String originalTimeZone = vmSchedule.getTimeZone();
|
||||
final Date originalStartDate = vmSchedule.getStartDate();
|
||||
final Date originalEndDate = vmSchedule.getEndDate();
|
||||
|
||||
TimeZone timeZone;
|
||||
String timeZoneId;
|
||||
if (cmdTimeZone != null) {
|
||||
timeZone = TimeZone.getTimeZone(cmdTimeZone);
|
||||
timeZoneId = timeZone.getID();
|
||||
if (!timeZoneId.equals(cmdTimeZone)) {
|
||||
logger.warn("Using timezone [{}] for running the schedule [{}] for VM {}, as an equivalent of [{}].",
|
||||
timeZoneId, vmSchedule.getSchedule(), userVmManager.getUserVm(vmSchedule.getVmId()), cmdTimeZone);
|
||||
}
|
||||
vmSchedule.setTimeZone(timeZoneId);
|
||||
} else {
|
||||
timeZoneId = vmSchedule.getTimeZone();
|
||||
timeZone = TimeZone.getTimeZone(timeZoneId);
|
||||
}
|
||||
|
||||
Date startDate = vmSchedule.getStartDate().before(DateUtils.addMinutes(new Date(), 1)) ? DateUtils.addMinutes(new Date(), 1) : vmSchedule.getStartDate();
|
||||
Date endDate = vmSchedule.getEndDate();
|
||||
if (cmdEndDate != null) {
|
||||
endDate = Date.from(DateUtil.getZoneDateTime(cmdEndDate, timeZone.toZoneId()).toInstant());
|
||||
}
|
||||
|
||||
if (cmdStartDate != null) {
|
||||
startDate = Date.from(DateUtil.getZoneDateTime(cmdStartDate, timeZone.toZoneId()).toInstant());
|
||||
}
|
||||
|
||||
if (ObjectUtils.anyNotNull(cmdStartDate, cmdEndDate, cmdTimeZone) &&
|
||||
(!Objects.equals(originalTimeZone, timeZoneId) ||
|
||||
!Objects.equals(originalStartDate, startDate) ||
|
||||
!Objects.equals(originalEndDate, endDate))) {
|
||||
validateStartDateEndDate(Objects.requireNonNullElse(startDate, DateUtils.addMinutes(new Date(), 1)),
|
||||
endDate, timeZone);
|
||||
}
|
||||
|
||||
if (enabled != null) {
|
||||
vmSchedule.setEnabled(enabled);
|
||||
}
|
||||
if (description != null) {
|
||||
vmSchedule.setDescription(description);
|
||||
}
|
||||
if (cmdEndDate != null) {
|
||||
vmSchedule.setEndDate(endDate);
|
||||
}
|
||||
if (cmdStartDate != null) {
|
||||
vmSchedule.setStartDate(startDate);
|
||||
}
|
||||
vmSchedule.setSchedule(cronExpression.toString());
|
||||
|
||||
return Transaction.execute((TransactionCallback<VMScheduleResponse>) status -> {
|
||||
vmScheduleDao.update(cmd.getId(), vmSchedule);
|
||||
vmScheduler.updateScheduledJob(vmSchedule);
|
||||
CallContext.current().setEventResourceId(vm.getId());
|
||||
CallContext.current().setEventResourceType(ApiCommandResourceType.VirtualMachine);
|
||||
return createResponse(vmSchedule);
|
||||
});
|
||||
}
|
||||
|
||||
void validateStartDateEndDate(Date startDate, Date endDate, TimeZone tz) {
|
||||
ZonedDateTime now = ZonedDateTime.now(tz.toZoneId());
|
||||
ZonedDateTime zonedStartDate = ZonedDateTime.ofInstant(startDate.toInstant(), tz.toZoneId());
|
||||
|
||||
if (zonedStartDate.isBefore(now)) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid value for start date. Start date [%s] can't be before current time [%s].", zonedStartDate, now));
|
||||
}
|
||||
|
||||
if (endDate != null) {
|
||||
ZonedDateTime zonedEndDate = ZonedDateTime.ofInstant(endDate.toInstant(), tz.toZoneId());
|
||||
if (zonedEndDate.isBefore(now)) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid value for end date. End date [%s] can't be before current time [%s].", zonedEndDate, now));
|
||||
}
|
||||
if (zonedEndDate.isBefore(zonedStartDate)) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid value for end date. End date [%s] can't be before start date [%s].", zonedEndDate, zonedStartDate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VM_SCHEDULE_DELETE, eventDescription = "Deleting VM Schedule for VM")
|
||||
public long removeScheduleByVmId(long vmId, boolean expunge) {
|
||||
SearchCriteria<VMScheduleVO> sc = vmScheduleDao.getSearchCriteriaForVMId(vmId);
|
||||
List<VMScheduleVO> vmSchedules = vmScheduleDao.search(sc, null);
|
||||
List<Long> ids = new ArrayList<>();
|
||||
for (final VMScheduleVO vmSchedule : vmSchedules) {
|
||||
ids.add(vmSchedule.getId());
|
||||
}
|
||||
vmScheduler.removeScheduledJobs(ids);
|
||||
if (expunge) {
|
||||
return vmScheduleDao.expunge(sc);
|
||||
}
|
||||
CallContext.current().setEventResourceId(vmId);
|
||||
CallContext.current().setEventResourceType(ApiCommandResourceType.VirtualMachine);
|
||||
return vmScheduleDao.remove(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_VM_SCHEDULE_DELETE, eventDescription = "Deleting VM Schedule")
|
||||
public Long removeSchedule(DeleteVMScheduleCmd cmd) {
|
||||
VirtualMachine vm = userVmManager.getUserVm(cmd.getVmId());
|
||||
accountManager.checkAccess(CallContext.current().getCallingAccount(), null, false, vm);
|
||||
|
||||
List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds());
|
||||
|
||||
if (ids.isEmpty()) {
|
||||
throw new InvalidParameterValueException("Either id or ids parameter must be specified");
|
||||
}
|
||||
return Transaction.execute((TransactionCallback<Long>) status -> {
|
||||
vmScheduler.removeScheduledJobs(ids);
|
||||
CallContext.current().setEventResourceId(vm.getId());
|
||||
CallContext.current().setEventResourceType(ApiCommandResourceType.VirtualMachine);
|
||||
return vmScheduleDao.removeSchedulesForVmIdAndIds(vm.getId(), ids);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule;
|
||||
|
||||
import com.cloud.utils.component.Manager;
|
||||
import com.cloud.utils.concurrency.Scheduler;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public interface VMScheduler extends Manager, Scheduler {
|
||||
ConfigKey<Integer> VMScheduledJobExpireInterval = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, "vmscheduler.jobs.expire.interval", "30", "VM Scheduler expire interval in days", true);
|
||||
|
||||
void removeScheduledJobs(List<Long> vmScheduleIds);
|
||||
|
||||
void updateScheduledJob(VMScheduleVO vmSchedule);
|
||||
|
||||
Date scheduleNextJob(VMScheduleVO vmSchedule, Date timestamp);
|
||||
}
|
||||
|
|
@ -1,420 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule;
|
||||
|
||||
import com.cloud.api.ApiGsonHelper;
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.db.GlobalLock;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.google.common.primitives.Longs;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.StopVMCmd;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
||||
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
|
||||
import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
|
||||
import org.apache.cloudstack.vm.schedule.dao.VMScheduleDao;
|
||||
import org.apache.cloudstack.vm.schedule.dao.VMScheduledJobDao;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.springframework.scheduling.support.CronExpression;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.EntityExistsException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class VMSchedulerImpl extends ManagerBase implements VMScheduler, Configurable {
|
||||
@Inject
|
||||
private VMScheduledJobDao vmScheduledJobDao;
|
||||
@Inject
|
||||
private VMScheduleDao vmScheduleDao;
|
||||
@Inject
|
||||
private UserVmManager userVmManager;
|
||||
@Inject
|
||||
private AsyncJobManager asyncJobManager;
|
||||
private AsyncJobDispatcher asyncJobDispatcher;
|
||||
private Timer vmSchedulerTimer;
|
||||
private Date currentTimestamp;
|
||||
|
||||
private EnumMap<VMSchedule.Action, String> actionEventMap = new EnumMap<>(VMSchedule.Action.class);
|
||||
|
||||
public AsyncJobDispatcher getAsyncJobDispatcher() {
|
||||
return asyncJobDispatcher;
|
||||
}
|
||||
|
||||
public void setAsyncJobDispatcher(final AsyncJobDispatcher dispatcher) {
|
||||
asyncJobDispatcher = dispatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey<?>[]{VMScheduledJobExpireInterval};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfigComponentName() {
|
||||
return VMScheduler.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScheduledJobs(List<Long> vmScheduleIds) {
|
||||
if (vmScheduleIds == null || vmScheduleIds.isEmpty()) {
|
||||
logger.debug("Removed 0 scheduled jobs");
|
||||
return;
|
||||
}
|
||||
Date now = new Date();
|
||||
int rowsRemoved = vmScheduledJobDao.expungeJobsForSchedules(vmScheduleIds, now);
|
||||
logger.debug(String.format("Removed %s VM scheduled jobs", rowsRemoved));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateScheduledJob(VMScheduleVO vmSchedule) {
|
||||
removeScheduledJobs(Longs.asList(vmSchedule.getId()));
|
||||
scheduleNextJob(vmSchedule, new Date());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date scheduleNextJob(VMScheduleVO vmSchedule, Date timestamp) {
|
||||
if (!vmSchedule.getEnabled()) {
|
||||
logger.debug("VM Schedule {} for VM {} with id {} is disabled. Not scheduling next job.",
|
||||
vmSchedule::toString, () -> userVmManager.getUserVm(vmSchedule.getVmId()), vmSchedule::getVmId);
|
||||
return null;
|
||||
}
|
||||
|
||||
CronExpression cron = DateUtil.parseSchedule(vmSchedule.getSchedule());
|
||||
Date startDate = vmSchedule.getStartDate();
|
||||
Date endDate = vmSchedule.getEndDate();
|
||||
VirtualMachine vm = userVmManager.getUserVm(vmSchedule.getVmId());
|
||||
|
||||
if (vm == null) {
|
||||
logger.info("VM id={} is removed. Disabling VM schedule {}.", vmSchedule.getVmId(), vmSchedule);
|
||||
vmSchedule.setEnabled(false);
|
||||
vmScheduleDao.persist(vmSchedule);
|
||||
return null;
|
||||
}
|
||||
|
||||
ZonedDateTime now;
|
||||
if (timestamp != null) {
|
||||
now = ZonedDateTime.ofInstant(timestamp.toInstant(), vmSchedule.getTimeZoneId());
|
||||
} else {
|
||||
now = ZonedDateTime.now(vmSchedule.getTimeZoneId());
|
||||
}
|
||||
ZonedDateTime zonedStartDate = ZonedDateTime.ofInstant(startDate.toInstant(), vmSchedule.getTimeZoneId());
|
||||
ZonedDateTime zonedEndDate = null;
|
||||
if (endDate != null) {
|
||||
zonedEndDate = ZonedDateTime.ofInstant(endDate.toInstant(), vmSchedule.getTimeZoneId());
|
||||
}
|
||||
if (zonedEndDate != null && now.isAfter(zonedEndDate)) {
|
||||
logger.info("End time is less than current time. Disabling VM schedule {} for VM {}.", vmSchedule, vm);
|
||||
vmSchedule.setEnabled(false);
|
||||
vmScheduleDao.persist(vmSchedule);
|
||||
return null;
|
||||
}
|
||||
|
||||
ZonedDateTime ts = null;
|
||||
if (zonedStartDate.isAfter(now)) {
|
||||
ts = cron.next(zonedStartDate);
|
||||
} else {
|
||||
ts = cron.next(now);
|
||||
}
|
||||
|
||||
if (ts == null) {
|
||||
logger.info("No next schedule found. Disabling VM schedule {} for VM {}.", vmSchedule, vm);
|
||||
vmSchedule.setEnabled(false);
|
||||
vmScheduleDao.persist(vmSchedule);
|
||||
return null;
|
||||
}
|
||||
|
||||
Date scheduledDateTime = Date.from(ts.toInstant());
|
||||
VMScheduledJobVO scheduledJob = vmScheduledJobDao.findByScheduleAndTimestamp(vmSchedule.getId(), scheduledDateTime);
|
||||
if (scheduledJob != null) {
|
||||
logger.trace("Job is already scheduled for schedule {} at {}", vmSchedule, scheduledDateTime);
|
||||
return scheduledDateTime;
|
||||
}
|
||||
|
||||
scheduledJob = new VMScheduledJobVO(vmSchedule.getVmId(), vmSchedule.getId(), vmSchedule.getAction(), scheduledDateTime);
|
||||
try {
|
||||
vmScheduledJobDao.persist(scheduledJob);
|
||||
ActionEventUtils.onScheduledActionEvent(User.UID_SYSTEM, vm.getAccountId(), actionEventMap.get(vmSchedule.getAction()),
|
||||
String.format("Scheduled action (%s) [vm: %s, schedule: %s] at %s", vmSchedule.getAction(), vm, vmSchedule, scheduledDateTime),
|
||||
vm.getId(), ApiCommandResourceType.VirtualMachine.toString(), true, 0);
|
||||
} catch (EntityExistsException exception) {
|
||||
logger.debug("Job is already scheduled.");
|
||||
}
|
||||
return scheduledDateTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
actionEventMap.put(VMSchedule.Action.START, EventTypes.EVENT_VM_SCHEDULE_START);
|
||||
actionEventMap.put(VMSchedule.Action.STOP, EventTypes.EVENT_VM_SCHEDULE_STOP);
|
||||
actionEventMap.put(VMSchedule.Action.REBOOT, EventTypes.EVENT_VM_SCHEDULE_REBOOT);
|
||||
actionEventMap.put(VMSchedule.Action.FORCE_STOP, EventTypes.EVENT_VM_SCHEDULE_FORCE_STOP);
|
||||
actionEventMap.put(VMSchedule.Action.FORCE_REBOOT, EventTypes.EVENT_VM_SCHEDULE_FORCE_REBOOT);
|
||||
|
||||
// Adding 1 minute to currentTimestamp to ensure that
|
||||
// jobs which were to be run at current time, doesn't cause issues
|
||||
currentTimestamp = DateUtils.addMinutes(new Date(), 1);
|
||||
scheduleNextJobs(currentTimestamp);
|
||||
|
||||
final TimerTask schedulerPollTask = new ManagedContextTimerTask() {
|
||||
@Override
|
||||
protected void runInContext() {
|
||||
try {
|
||||
poll(new Date());
|
||||
} catch (final Throwable t) {
|
||||
logger.warn("Catch throwable in VM scheduler ", t);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vmSchedulerTimer = new Timer("VMSchedulerPollTask");
|
||||
vmSchedulerTimer.scheduleAtFixedRate(schedulerPollTask, 5000L, 60 * 1000L);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void poll(Date timestamp) {
|
||||
currentTimestamp = DateUtils.round(timestamp, Calendar.MINUTE);
|
||||
String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp);
|
||||
logger.debug(String.format("VM scheduler.poll is being called at %s", displayTime));
|
||||
|
||||
GlobalLock scanLock = GlobalLock.getInternLock("vmScheduler.poll");
|
||||
try {
|
||||
if (scanLock.lock(30)) {
|
||||
try {
|
||||
scheduleNextJobs(currentTimestamp);
|
||||
} finally {
|
||||
scanLock.unlock();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
scanLock.releaseRef();
|
||||
}
|
||||
|
||||
scanLock = GlobalLock.getInternLock("vmScheduler.poll");
|
||||
try {
|
||||
if (scanLock.lock(30)) {
|
||||
try {
|
||||
startJobs(); // Create async job and update scheduled job
|
||||
} finally {
|
||||
scanLock.unlock();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
scanLock.releaseRef();
|
||||
}
|
||||
|
||||
try {
|
||||
cleanupVMScheduledJobs();
|
||||
} catch (Exception e) {
|
||||
logger.warn("Error in cleaning up vm scheduled jobs", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleNextJobs(Date timestamp) {
|
||||
for (final VMScheduleVO schedule : vmScheduleDao.listAllActiveSchedules()) {
|
||||
try {
|
||||
scheduleNextJob(schedule, timestamp);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Error in scheduling next job for schedule {}", schedule, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete scheduled jobs before vm.scheduler.expire.interval days
|
||||
*/
|
||||
private void cleanupVMScheduledJobs() {
|
||||
Date deleteBeforeDate = DateUtils.addDays(currentTimestamp, -1 * VMScheduledJobExpireInterval.value());
|
||||
int rowsRemoved = vmScheduledJobDao.expungeJobsBefore(deleteBeforeDate);
|
||||
logger.info(String.format("Cleaned up %d VM scheduled job entries", rowsRemoved));
|
||||
}
|
||||
|
||||
void executeJobs(Map<Long, VMScheduledJob> jobsToExecute) {
|
||||
String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp);
|
||||
|
||||
for (Map.Entry<Long, VMScheduledJob> entry : jobsToExecute.entrySet()) {
|
||||
VMScheduledJob vmScheduledJob = entry.getValue();
|
||||
VirtualMachine vm = userVmManager.getUserVm(vmScheduledJob.getVmId());
|
||||
|
||||
VMScheduledJobVO tmpVMScheduleJob = null;
|
||||
try {
|
||||
if (logger.isDebugEnabled()) {
|
||||
final Date scheduledTimestamp = vmScheduledJob.getScheduledTime();
|
||||
displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp);
|
||||
logger.debug("Executing {} for VM {} for scheduled job: {} at {}",
|
||||
vmScheduledJob.getAction(), vm, vmScheduledJob, displayTime);
|
||||
}
|
||||
|
||||
tmpVMScheduleJob = vmScheduledJobDao.acquireInLockTable(vmScheduledJob.getId());
|
||||
Long jobId = processJob(vmScheduledJob, vm);
|
||||
if (jobId != null) {
|
||||
tmpVMScheduleJob.setAsyncJobId(jobId);
|
||||
vmScheduledJobDao.update(vmScheduledJob.getId(), tmpVMScheduleJob);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
logger.warn("Executing scheduled job {} failed due to {}", vmScheduledJob, e);
|
||||
} finally {
|
||||
if (tmpVMScheduleJob != null) {
|
||||
vmScheduledJobDao.releaseFromLockTable(vmScheduledJob.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Long processJob(VMScheduledJob vmScheduledJob, VirtualMachine vm) {
|
||||
if (!Arrays.asList(VirtualMachine.State.Running, VirtualMachine.State.Stopped).contains(vm.getState())) {
|
||||
logger.info("Skipping action ({}) for [vm: {}, scheduled job: {}] because VM is invalid state: {}",
|
||||
vmScheduledJob.getAction(), vm, vmScheduledJob, vm.getState());
|
||||
return null;
|
||||
}
|
||||
|
||||
final Long eventId = ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, vm.getAccountId(), null,
|
||||
actionEventMap.get(vmScheduledJob.getAction()), true,
|
||||
String.format("Executing action (%s) for VM: %s", vmScheduledJob.getAction(), vm),
|
||||
vm.getId(), ApiCommandResourceType.VirtualMachine.toString(), 0);
|
||||
|
||||
if (vm.getState() == VirtualMachine.State.Running) {
|
||||
switch (vmScheduledJob.getAction()) {
|
||||
case STOP:
|
||||
return executeStopVMJob(vm, false, eventId);
|
||||
case FORCE_STOP:
|
||||
return executeStopVMJob(vm, true, eventId);
|
||||
case REBOOT:
|
||||
return executeRebootVMJob(vm, false, eventId);
|
||||
case FORCE_REBOOT:
|
||||
return executeRebootVMJob(vm, true, eventId);
|
||||
}
|
||||
} else if (vm.getState() == VirtualMachine.State.Stopped && vmScheduledJob.getAction() == VMSchedule.Action.START) {
|
||||
return executeStartVMJob(vm, eventId);
|
||||
}
|
||||
|
||||
logger.warn("Skipping action ({}) for [vm: {}, scheduled job: {}] because VM is in state: {}",
|
||||
vmScheduledJob.getAction(), vm, vmScheduledJob, vm.getState());
|
||||
return null;
|
||||
}
|
||||
|
||||
private void skipJobs(Map<Long, VMScheduledJob> jobsToExecute, Map<Long, List<VMScheduledJob>> jobsNotToExecute) {
|
||||
for (Map.Entry<Long, List<VMScheduledJob>> entry : jobsNotToExecute.entrySet()) {
|
||||
Long vmId = entry.getKey();
|
||||
List<VMScheduledJob> skippedVmScheduledJobVOS = entry.getValue();
|
||||
VirtualMachine vm = userVmManager.getUserVm(vmId);
|
||||
for (final VMScheduledJob skippedVmScheduledJobVO : skippedVmScheduledJobVOS) {
|
||||
VMScheduledJob scheduledJob = jobsToExecute.get(vmId);
|
||||
logger.info("Skipping scheduled job {} for vm {} because of conflict with another scheduled job {}",
|
||||
skippedVmScheduledJobVO, vm, scheduledJob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create async jobs for VM scheduled jobs
|
||||
*/
|
||||
private void startJobs() {
|
||||
String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp);
|
||||
|
||||
final List<VMScheduledJobVO> vmScheduledJobs = vmScheduledJobDao.listJobsToStart(currentTimestamp);
|
||||
logger.debug(String.format("Got %d scheduled jobs to be executed at %s", vmScheduledJobs.size(), displayTime));
|
||||
|
||||
Map<Long, VMScheduledJob> jobsToExecute = new HashMap<>();
|
||||
Map<Long, List<VMScheduledJob>> jobsNotToExecute = new HashMap<>();
|
||||
for (final VMScheduledJobVO vmScheduledJobVO : vmScheduledJobs) {
|
||||
long vmId = vmScheduledJobVO.getVmId();
|
||||
if (jobsToExecute.get(vmId) == null) {
|
||||
jobsToExecute.put(vmId, vmScheduledJobVO);
|
||||
} else {
|
||||
jobsNotToExecute.computeIfAbsent(vmId, k -> new ArrayList<>()).add(vmScheduledJobVO);
|
||||
}
|
||||
}
|
||||
|
||||
executeJobs(jobsToExecute);
|
||||
skipJobs(jobsToExecute, jobsNotToExecute);
|
||||
}
|
||||
|
||||
long executeStartVMJob(VirtualMachine vm, long eventId) {
|
||||
final Map<String, String> params = new HashMap<>();
|
||||
params.put(ApiConstants.ID, String.valueOf(vm.getId()));
|
||||
params.put("ctxUserId", "1");
|
||||
params.put("ctxAccountId", String.valueOf(vm.getAccountId()));
|
||||
params.put(ApiConstants.CTX_START_EVENT_ID, String.valueOf(eventId));
|
||||
|
||||
final StartVMCmd cmd = new StartVMCmd();
|
||||
ComponentContext.inject(cmd);
|
||||
|
||||
AsyncJobVO job = new AsyncJobVO("", User.UID_SYSTEM, vm.getAccountId(), StartVMCmd.class.getName(), ApiGsonHelper.getBuilder().create().toJson(params), vm.getId(), cmd.getApiResourceType() != null ? cmd.getApiResourceType().toString() : null, null);
|
||||
job.setDispatcher(asyncJobDispatcher.getName());
|
||||
|
||||
return asyncJobManager.submitAsyncJob(job);
|
||||
}
|
||||
|
||||
long executeStopVMJob(VirtualMachine vm, boolean isForced, long eventId) {
|
||||
final Map<String, String> params = new HashMap<>();
|
||||
params.put(ApiConstants.ID, String.valueOf(vm.getId()));
|
||||
params.put("ctxUserId", "1");
|
||||
params.put("ctxAccountId", String.valueOf(vm.getAccountId()));
|
||||
params.put(ApiConstants.CTX_START_EVENT_ID, String.valueOf(eventId));
|
||||
params.put(ApiConstants.FORCED, String.valueOf(isForced));
|
||||
|
||||
final StopVMCmd cmd = new StopVMCmd();
|
||||
ComponentContext.inject(cmd);
|
||||
|
||||
AsyncJobVO job = new AsyncJobVO("", User.UID_SYSTEM, vm.getAccountId(), StopVMCmd.class.getName(), ApiGsonHelper.getBuilder().create().toJson(params), vm.getId(), cmd.getApiResourceType() != null ? cmd.getApiResourceType().toString() : null, null);
|
||||
job.setDispatcher(asyncJobDispatcher.getName());
|
||||
|
||||
return asyncJobManager.submitAsyncJob(job);
|
||||
}
|
||||
|
||||
long executeRebootVMJob(VirtualMachine vm, boolean isForced, long eventId) {
|
||||
final Map<String, String> params = new HashMap<>();
|
||||
params.put(ApiConstants.ID, String.valueOf(vm.getId()));
|
||||
params.put("ctxUserId", "1");
|
||||
params.put("ctxAccountId", String.valueOf(vm.getAccountId()));
|
||||
params.put(ApiConstants.CTX_START_EVENT_ID, String.valueOf(eventId));
|
||||
params.put(ApiConstants.FORCED, String.valueOf(isForced));
|
||||
|
||||
final RebootVMCmd cmd = new RebootVMCmd();
|
||||
ComponentContext.inject(cmd);
|
||||
|
||||
AsyncJobVO job = new AsyncJobVO("", User.UID_SYSTEM, vm.getAccountId(), RebootVMCmd.class.getName(), ApiGsonHelper.getBuilder().create().toJson(params), vm.getId(), cmd.getApiResourceType() != null ? cmd.getApiResourceType().toString() : null, null);
|
||||
job.setDispatcher(asyncJobDispatcher.getName());
|
||||
|
||||
return asyncJobManager.submitAsyncJob(job);
|
||||
}
|
||||
}
|
||||
|
|
@ -372,8 +372,8 @@
|
|||
<bean id="bucketApiServiceImpl" class="org.apache.cloudstack.storage.object.BucketApiServiceImpl" />
|
||||
<bean id="routedIpv4ManagerImpl" class="org.apache.cloudstack.network.RoutedIpv4ManagerImpl" />
|
||||
|
||||
<bean id="VMScheduleManagerImpl" class="org.apache.cloudstack.vm.schedule.VMScheduleManagerImpl" />
|
||||
<bean id="VMSchedulerImpl" class="org.apache.cloudstack.vm.schedule.VMSchedulerImpl">
|
||||
<bean id="ResourceScheduleManagerImpl" class="org.apache.cloudstack.schedule.ResourceScheduleManagerImpl" />
|
||||
<bean id="VMScheduleWorker" class="org.apache.cloudstack.schedule.vm.VMScheduleWorker">
|
||||
<property name="asyncJobDispatcher" ref="ApiAsyncJobDispatcher" />
|
||||
</bean>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.schedule;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.EntityManager;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.ResourceScheduleResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.schedule.dao.ResourceScheduleDao;
|
||||
import org.apache.cloudstack.schedule.dao.ResourceScheduleDetailsDao;
|
||||
import org.apache.cloudstack.schedule.vm.VMScheduleAction;
|
||||
import org.apache.cloudstack.schedule.vm.VMScheduleWorker;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class ResourceScheduleManagerImplTest {
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
ResourceScheduleManagerImpl resourceScheduleManager = new ResourceScheduleManagerImpl();
|
||||
|
||||
@Mock
|
||||
ResourceScheduleDao resourceScheduleDao;
|
||||
|
||||
@Mock
|
||||
ResourceScheduleDetailsDao resourceScheduleDetailsDao;
|
||||
|
||||
@Mock
|
||||
VMScheduleWorker vmScheduleWorker;
|
||||
|
||||
@Mock
|
||||
UserVmManager userVmManager;
|
||||
|
||||
@Mock
|
||||
AccountManager accountManager;
|
||||
|
||||
@Mock
|
||||
EntityManager entityManager;
|
||||
|
||||
private AutoCloseable closeable;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
closeable = MockitoAnnotations.openMocks(this);
|
||||
Account callingAccount = Mockito.mock(Account.class);
|
||||
User callingUser = Mockito.mock(User.class);
|
||||
CallContext.register(callingUser, callingAccount);
|
||||
|
||||
Mockito.when(vmScheduleWorker.getApiResourceType()).thenReturn(ApiCommandResourceType.VirtualMachine);
|
||||
Map<ApiCommandResourceType, BaseScheduleWorker> workerMap = new HashMap<>();
|
||||
workerMap.put(ApiCommandResourceType.VirtualMachine, vmScheduleWorker);
|
||||
ReflectionTestUtils.setField(resourceScheduleManager, "workerMap", workerMap);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
closeable.close();
|
||||
}
|
||||
|
||||
private void validateResponse(ResourceScheduleResponse response, ResourceScheduleVO schedule, VirtualMachine vm) {
|
||||
assertNotNull(response);
|
||||
Assert.assertEquals(schedule.getUuid(), ReflectionTestUtils.getField(response, "id"));
|
||||
Assert.assertEquals(schedule.getResourceType(), ReflectionTestUtils.getField(response, "resourceType"));
|
||||
Assert.assertEquals(vm.getUuid(), ReflectionTestUtils.getField(response, "resourceId"));
|
||||
Assert.assertEquals(schedule.getSchedule(), ReflectionTestUtils.getField(response, "schedule"));
|
||||
Assert.assertEquals(schedule.getTimeZone(), ReflectionTestUtils.getField(response, "timeZone"));
|
||||
ResourceSchedule.Action actionField = (ResourceSchedule.Action) ReflectionTestUtils.getField(response, "action");
|
||||
Assert.assertEquals(schedule.getActionName(), actionField == null ? null : actionField.name());
|
||||
Assert.assertEquals(schedule.getStartDate(), ReflectionTestUtils.getField(response, "startDate"));
|
||||
Assert.assertEquals(schedule.getEndDate(), ReflectionTestUtils.getField(response, "endDate"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSchedule() {
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
ResourceScheduleVO schedule = Mockito.mock(ResourceScheduleVO.class);
|
||||
Account ownerAccount = Mockito.mock(Account.class);
|
||||
|
||||
Mockito.when(vm.getUuid()).thenReturn(UUID.randomUUID().toString());
|
||||
Mockito.when(vm.getId()).thenReturn(1L);
|
||||
Mockito.when(entityManager.findByUuid(VirtualMachine.class, "1")).thenReturn(null);
|
||||
Mockito.when(entityManager.findById(VirtualMachine.class, 1L)).thenReturn(vm);
|
||||
Mockito.when(vmScheduleWorker.isResourceValid(1L)).thenReturn(true);
|
||||
Mockito.when(vmScheduleWorker.getEntityOwnerId(1L)).thenReturn(2L);
|
||||
Mockito.when(vmScheduleWorker.parseAction("START")).thenReturn(VMScheduleAction.START);
|
||||
Mockito.when(accountManager.getAccount(2L)).thenReturn(ownerAccount);
|
||||
Mockito.when(resourceScheduleDao.persist(Mockito.any(ResourceScheduleVO.class))).thenReturn(schedule);
|
||||
Mockito.when(schedule.getResourceType()).thenReturn(ApiCommandResourceType.VirtualMachine);
|
||||
Mockito.when(schedule.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(schedule.getActionName()).thenReturn("START");
|
||||
Mockito.when(vmScheduleWorker.parseAction((String) Mockito.isNull())).thenReturn(null);
|
||||
|
||||
ResourceScheduleResponse response = resourceScheduleManager.createSchedule(
|
||||
ApiCommandResourceType.VirtualMachine, "1", null,
|
||||
"0 0 * * *", "UTC", "START",
|
||||
DateUtils.addDays(new Date(), 1), DateUtils.addDays(new Date(), 2),
|
||||
true, null);
|
||||
|
||||
Mockito.verify(resourceScheduleDao, Mockito.times(1)).persist(Mockito.any(ResourceScheduleVO.class));
|
||||
validateResponse(response, schedule, vm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createResponse() {
|
||||
ResourceScheduleVO schedule = Mockito.mock(ResourceScheduleVO.class);
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
|
||||
Mockito.when(vm.getUuid()).thenReturn(UUID.randomUUID().toString());
|
||||
Mockito.when(schedule.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(schedule.getResourceType()).thenReturn(ApiCommandResourceType.VirtualMachine);
|
||||
Mockito.when(schedule.getActionName()).thenReturn("START");
|
||||
Mockito.when(vmScheduleWorker.parseAction("START")).thenReturn(VMScheduleAction.START);
|
||||
Mockito.when(entityManager.findById(VirtualMachine.class, 1L)).thenReturn(vm);
|
||||
|
||||
ResourceScheduleResponse response = resourceScheduleManager.createResponse(schedule, null);
|
||||
validateResponse(response, schedule, vm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listSchedule() {
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
ResourceScheduleVO schedule1 = Mockito.mock(ResourceScheduleVO.class);
|
||||
ResourceScheduleVO schedule2 = Mockito.mock(ResourceScheduleVO.class);
|
||||
List<ResourceScheduleVO> scheduleList = new ArrayList<>();
|
||||
scheduleList.add(schedule1);
|
||||
scheduleList.add(schedule2);
|
||||
|
||||
Mockito.when(vm.getUuid()).thenReturn(UUID.randomUUID().toString());
|
||||
Mockito.when(vm.getId()).thenReturn(1L);
|
||||
Mockito.when(entityManager.findByUuid(VirtualMachine.class, "1")).thenReturn(null);
|
||||
Mockito.when(entityManager.findById(VirtualMachine.class, 1L)).thenReturn(vm);
|
||||
Mockito.when(vmScheduleWorker.getEntityOwnerId(1L)).thenReturn(2L);
|
||||
Mockito.when(accountManager.getAccount(2L)).thenReturn(Mockito.mock(Account.class));
|
||||
|
||||
Mockito.when(resourceScheduleDao.searchAndCount(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.any(), Mockito.any(), Mockito.any())
|
||||
).thenReturn(new Pair<>(scheduleList, scheduleList.size()));
|
||||
for (ResourceScheduleVO schedule : scheduleList) {
|
||||
Mockito.when(schedule.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(schedule.getResourceType()).thenReturn(ApiCommandResourceType.VirtualMachine);
|
||||
}
|
||||
|
||||
ListResponse<ResourceScheduleResponse> responseList = resourceScheduleManager.listSchedule(
|
||||
null, null, ApiCommandResourceType.VirtualMachine, "1", null, null, 0L, 100L);
|
||||
|
||||
Mockito.verify(resourceScheduleDao, Mockito.times(1)).searchAndCount(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.any(), Mockito.any(), Mockito.any());
|
||||
assertNotNull(responseList);
|
||||
Assert.assertEquals(2, (int) responseList.getCount());
|
||||
Assert.assertEquals(2, responseList.getResponses().size());
|
||||
|
||||
for (int i = 0; i < responseList.getResponses().size(); i++) {
|
||||
validateResponse(responseList.getResponses().get(i), scheduleList.get(i), vm);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSchedule() {
|
||||
ResourceScheduleVO schedule = Mockito.mock(ResourceScheduleVO.class);
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
|
||||
Mockito.when(vm.getUuid()).thenReturn(UUID.randomUUID().toString());
|
||||
Mockito.when(resourceScheduleDao.findById(Mockito.anyLong())).thenReturn(schedule);
|
||||
Mockito.when(resourceScheduleDao.update(Mockito.eq(1L), Mockito.any(ResourceScheduleVO.class))).thenReturn(true);
|
||||
Mockito.when(schedule.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(schedule.getResourceType()).thenReturn(ApiCommandResourceType.VirtualMachine);
|
||||
Mockito.when(schedule.getStartDate()).thenReturn(DateUtils.addDays(new Date(), 1));
|
||||
Mockito.when(schedule.getActionName()).thenReturn("START");
|
||||
Mockito.when(vmScheduleWorker.getEntityOwnerId(1L)).thenReturn(2L);
|
||||
Mockito.when(vmScheduleWorker.parseAction("START")).thenReturn(VMScheduleAction.START);
|
||||
Mockito.when(accountManager.getAccount(2L)).thenReturn(Mockito.mock(Account.class));
|
||||
Mockito.when(entityManager.findById(VirtualMachine.class, 1L)).thenReturn(vm);
|
||||
|
||||
ResourceScheduleResponse response = resourceScheduleManager.updateSchedule(
|
||||
1L, null, "0 0 * * *", "UTC",
|
||||
DateUtils.addDays(new Date(), 1), DateUtils.addDays(new Date(), 2), null, null);
|
||||
Mockito.verify(resourceScheduleDao, Mockito.times(1)).update(Mockito.eq(1L), Mockito.any(ResourceScheduleVO.class));
|
||||
|
||||
validateResponse(response, schedule, vm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeSchedulesForResource() {
|
||||
ResourceScheduleVO schedule1 = Mockito.mock(ResourceScheduleVO.class);
|
||||
ResourceScheduleVO schedule2 = Mockito.mock(ResourceScheduleVO.class);
|
||||
List<ResourceScheduleVO> scheduleList = new ArrayList<>();
|
||||
scheduleList.add(schedule1);
|
||||
scheduleList.add(schedule2);
|
||||
SearchCriteria<ResourceScheduleVO> sc = Mockito.mock(SearchCriteria.class);
|
||||
|
||||
Mockito.when(resourceScheduleDao.getSearchCriteriaForResource(
|
||||
ApiCommandResourceType.VirtualMachine, 1L)).thenReturn(sc);
|
||||
Mockito.when(resourceScheduleDao.search(sc, null)).thenReturn(scheduleList);
|
||||
Mockito.when(schedule1.getId()).thenReturn(1L);
|
||||
Mockito.when(schedule2.getId()).thenReturn(2L);
|
||||
Mockito.when(resourceScheduleDao.removeAllSchedulesForResource(Mockito.any(), Mockito.anyLong())).thenReturn(2L);
|
||||
|
||||
resourceScheduleManager.removeSchedulesForResource(ApiCommandResourceType.VirtualMachine, 1L);
|
||||
|
||||
Mockito.verify(resourceScheduleDao, Mockito.times(1)).removeAllSchedulesForResource(Mockito.any(), Mockito.anyLong());
|
||||
Mockito.verify(vmScheduleWorker, Mockito.times(1)).removeScheduledJobs(Mockito.anyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeSchedule() {
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
|
||||
Mockito.when(vm.getId()).thenReturn(1L);
|
||||
Mockito.when(entityManager.findByUuid(VirtualMachine.class, "1")).thenReturn(null);
|
||||
Mockito.when(entityManager.findById(VirtualMachine.class, 1L)).thenReturn(vm);
|
||||
Mockito.when(vmScheduleWorker.getEntityOwnerId(1L)).thenReturn(2L);
|
||||
Mockito.when(accountManager.getAccount(2L)).thenReturn(Mockito.mock(Account.class));
|
||||
Mockito.when(resourceScheduleDao.removeSchedulesForResourceAndIds(
|
||||
Mockito.any(), Mockito.anyLong(), Mockito.anyList())).thenReturn(1L);
|
||||
|
||||
ResourceScheduleVO schedule1 = Mockito.mock(ResourceScheduleVO.class);
|
||||
ResourceScheduleVO schedule2 = Mockito.mock(ResourceScheduleVO.class);
|
||||
List<ResourceScheduleVO> scheduleList = new ArrayList<>();
|
||||
scheduleList.add(schedule1);
|
||||
scheduleList.add(schedule2);
|
||||
|
||||
Mockito.when(resourceScheduleDao.searchAndCount(
|
||||
Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
|
||||
Mockito.any(), Mockito.any(), Mockito.any())
|
||||
).thenReturn(new Pair<>(scheduleList, scheduleList.size()));
|
||||
|
||||
Long rowsRemoved = resourceScheduleManager.removeSchedule(
|
||||
ApiCommandResourceType.VirtualMachine, "1", 10L, null);
|
||||
|
||||
Mockito.verify(resourceScheduleDao, Mockito.times(1)).removeSchedulesForResourceAndIds(
|
||||
Mockito.any(), Mockito.anyLong(), Mockito.anyList());
|
||||
Assert.assertEquals(1L, (long) rowsRemoved);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateStartDateEndDate() {
|
||||
// Valid scenario 1
|
||||
// Start date is before end date
|
||||
Date startDate = DateUtils.addDays(new Date(), 1);
|
||||
Date endDate = DateUtils.addDays(new Date(), 2);
|
||||
resourceScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
|
||||
|
||||
// Valid Scenario 2
|
||||
// Start date is before current date and end date is null
|
||||
endDate = null;
|
||||
resourceScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
|
||||
|
||||
// Invalid Scenario 2
|
||||
// Start date is before current date
|
||||
startDate = DateUtils.addDays(new Date(), -1);
|
||||
try {
|
||||
resourceScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
|
||||
Assert.fail("Should have thrown InvalidParameterValueException");
|
||||
} catch (InvalidParameterValueException e) {
|
||||
Assert.assertTrue(e.getMessage().contains("Invalid value for start date. Start date") &&
|
||||
e.getMessage().contains("can't be before current time"));
|
||||
}
|
||||
|
||||
// Invalid Scenario 2
|
||||
// Start date is after end date
|
||||
startDate = DateUtils.addDays(new Date(), 2);
|
||||
endDate = DateUtils.addDays(new Date(), 1);
|
||||
try {
|
||||
resourceScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
|
||||
Assert.fail("Should have thrown InvalidParameterValueException");
|
||||
} catch (InvalidParameterValueException e) {
|
||||
Assert.assertTrue(e.getMessage().contains("Invalid value for end date. End date") &&
|
||||
e.getMessage().contains("can't be before start date"));
|
||||
}
|
||||
|
||||
// Invalid Scenario 3
|
||||
// End date is before current date
|
||||
startDate = DateUtils.addDays(new Date(), 1);
|
||||
endDate = DateUtils.addDays(new Date(), -1);
|
||||
try {
|
||||
resourceScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
|
||||
Assert.fail("Should have thrown InvalidParameterValueException");
|
||||
} catch (InvalidParameterValueException e) {
|
||||
Assert.assertTrue(e.getMessage().contains("Invalid value for end date. End date") &&
|
||||
e.getMessage().contains("can't be before current time"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getConfigKeys() {
|
||||
ConfigKey<?>[] configKeys = resourceScheduleManager.getConfigKeys();
|
||||
Assert.assertEquals(1, configKeys.length);
|
||||
Assert.assertEquals(BaseScheduleWorker.ScheduledJobExpireInterval.key(), configKeys[0].key());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.schedule.vm;
|
||||
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.StopVMCmd;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
||||
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduleVO;
|
||||
import org.apache.cloudstack.schedule.ResourceScheduledJobVO;
|
||||
import org.apache.cloudstack.schedule.dao.ResourceScheduleDao;
|
||||
import org.apache.cloudstack.schedule.dao.ResourceScheduledJobDao;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import javax.persistence.EntityExistsException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class VMScheduleWorkerTest {
|
||||
@Spy
|
||||
@InjectMocks
|
||||
private VMScheduleWorker vmScheduleWorker = new VMScheduleWorker();
|
||||
|
||||
@Mock
|
||||
private UserVmManager userVmManager;
|
||||
|
||||
@Mock
|
||||
private ResourceScheduleDao resourceScheduleDao;
|
||||
|
||||
@Mock
|
||||
private ResourceScheduledJobDao resourceScheduledJobDao;
|
||||
|
||||
@Mock
|
||||
private AsyncJobManager asyncJobManager;
|
||||
|
||||
@Mock
|
||||
private AsyncJobDispatcher asyncJobDispatcher;
|
||||
|
||||
private AutoCloseable closeable;
|
||||
|
||||
private MockedStatic<ActionEventUtils> actionEventUtilsMocked;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
closeable = MockitoAnnotations.openMocks(this);
|
||||
actionEventUtilsMocked = Mockito.mockStatic(ActionEventUtils.class);
|
||||
Mockito.when(ActionEventUtils.onScheduledActionEvent(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(),
|
||||
Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyBoolean(),
|
||||
Mockito.anyLong()))
|
||||
.thenReturn(1L);
|
||||
Mockito.when(ActionEventUtils.onCompletedActionEvent(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(),
|
||||
Mockito.anyString(), Mockito.anyBoolean(),
|
||||
Mockito.anyString(),
|
||||
Mockito.anyLong(), Mockito.anyString(), Mockito.anyLong())).thenReturn(1L);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
actionEventUtilsMocked.close();
|
||||
closeable.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessJobRunning() {
|
||||
executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMScheduleAction.STOP);
|
||||
executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMScheduleAction.FORCE_STOP);
|
||||
executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMScheduleAction.REBOOT);
|
||||
executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMScheduleAction.FORCE_REBOOT);
|
||||
executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Stopped, VMScheduleAction.START);
|
||||
}
|
||||
|
||||
private void executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State state, VMScheduleAction action) {
|
||||
ResourceScheduledJobVO job = Mockito.mock(ResourceScheduledJobVO.class);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
Mockito.when(job.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(job.getActionName()).thenReturn(action.name());
|
||||
Mockito.when(vm.getState()).thenReturn(state);
|
||||
Mockito.when(userVmManager.getUserVm(1L)).thenReturn(vm);
|
||||
Mockito.doReturn(1L).when(vmScheduleWorker).submitAsyncJob(
|
||||
Mockito.any(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.any());
|
||||
|
||||
Long jobId = vmScheduleWorker.processJob(job);
|
||||
|
||||
Assert.assertNotNull(jobId);
|
||||
Assert.assertEquals(1L, (long) jobId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessJobInvalidAction() {
|
||||
ResourceScheduledJobVO job = Mockito.mock(ResourceScheduledJobVO.class);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
Mockito.when(job.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(job.getActionName()).thenReturn(VMScheduleAction.START.name());
|
||||
Mockito.when(vm.getState()).thenReturn(VirtualMachine.State.Running);
|
||||
Mockito.when(userVmManager.getUserVm(1L)).thenReturn(vm);
|
||||
|
||||
Long jobId = vmScheduleWorker.processJob(job);
|
||||
|
||||
Assert.assertNull(jobId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessJobVMInInvalidState() {
|
||||
ResourceScheduledJobVO job = Mockito.mock(ResourceScheduledJobVO.class);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
Mockito.when(job.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(job.getActionName()).thenReturn(VMScheduleAction.START.name());
|
||||
Mockito.when(vm.getState()).thenReturn(VirtualMachine.State.Unknown);
|
||||
Mockito.when(userVmManager.getUserVm(1L)).thenReturn(vm);
|
||||
|
||||
Long jobId = vmScheduleWorker.processJob(job);
|
||||
|
||||
Assert.assertNull(jobId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleScheduleExists() {
|
||||
Date now = DateUtils.setSeconds(new Date(), 0);
|
||||
Date startDate = DateUtils.addDays(now, 1);
|
||||
Date expectedScheduledTime = DateUtils.round(DateUtils.addMinutes(startDate, 1), Calendar.MINUTE);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
ResourceScheduleVO schedule = Mockito.mock(ResourceScheduleVO.class);
|
||||
Mockito.when(schedule.getEnabled()).thenReturn(true);
|
||||
Mockito.when(schedule.getSchedule()).thenReturn("* * * * *");
|
||||
Mockito.when(schedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("UTC").toZoneId());
|
||||
Mockito.when(schedule.getStartDate()).thenReturn(startDate);
|
||||
Mockito.when(schedule.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(schedule.getActionName()).thenReturn(VMScheduleAction.START.name());
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
|
||||
Mockito.when(resourceScheduledJobDao.persist(Mockito.any())).thenThrow(EntityExistsException.class);
|
||||
Date actualScheduledTime = vmScheduleWorker.scheduleNextJob(schedule, new Date());
|
||||
|
||||
Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleFutureSchedule() {
|
||||
Date now = DateUtils.setSeconds(new Date(), 0);
|
||||
Date startDate = DateUtils.addDays(now, 1);
|
||||
Date expectedScheduledTime = DateUtils.round(DateUtils.addMinutes(startDate, 1), Calendar.MINUTE);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
ResourceScheduleVO schedule = Mockito.mock(ResourceScheduleVO.class);
|
||||
Mockito.when(schedule.getEnabled()).thenReturn(true);
|
||||
Mockito.when(schedule.getSchedule()).thenReturn("* * * * *");
|
||||
Mockito.when(schedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("UTC").toZoneId());
|
||||
Mockito.when(schedule.getStartDate()).thenReturn(startDate);
|
||||
Mockito.when(schedule.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(schedule.getActionName()).thenReturn(VMScheduleAction.START.name());
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
|
||||
Date actualScheduledTime = vmScheduleWorker.scheduleNextJob(schedule, new Date());
|
||||
|
||||
Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleFutureScheduleWithTimeZoneChecks() throws Exception {
|
||||
String cron = "30 5 * * *";
|
||||
Date now = DateUtils.setSeconds(new Date(), 0);
|
||||
Date startDate = DateUtils.addDays(now, 1);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
ResourceScheduleVO schedule = Mockito.mock(ResourceScheduleVO.class);
|
||||
Mockito.when(schedule.getEnabled()).thenReturn(true);
|
||||
Mockito.when(schedule.getSchedule()).thenReturn(cron);
|
||||
Mockito.when(schedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("EST").toZoneId());
|
||||
Mockito.when(schedule.getStartDate()).thenReturn(startDate);
|
||||
Mockito.when(schedule.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(schedule.getActionName()).thenReturn(VMScheduleAction.START.name());
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
|
||||
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(startDate.toInstant(), schedule.getTimeZoneId());
|
||||
zonedDateTime = zonedDateTime.withHour(5).withMinute(30).withSecond(0).withNano(0);
|
||||
Date expectedScheduledTime = Date.from(zonedDateTime.toInstant());
|
||||
if (expectedScheduledTime.before(startDate)) {
|
||||
expectedScheduledTime = Date.from(zonedDateTime.plusDays(1).toInstant());
|
||||
}
|
||||
|
||||
Date actualScheduledTime = vmScheduleWorker.scheduleNextJob(schedule, new Date());
|
||||
Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleCurrentSchedule() {
|
||||
Date now = DateUtils.setSeconds(new Date(), 0);
|
||||
Date expectedScheduledTime = DateUtils.round(DateUtils.addMinutes(now, 1), Calendar.MINUTE);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
ResourceScheduleVO schedule = Mockito.mock(ResourceScheduleVO.class);
|
||||
Mockito.when(schedule.getEnabled()).thenReturn(true);
|
||||
Mockito.when(schedule.getSchedule()).thenReturn("* * * * *");
|
||||
Mockito.when(schedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("UTC").toZoneId());
|
||||
Mockito.when(schedule.getStartDate()).thenReturn(DateUtils.addDays(now, -1));
|
||||
Mockito.when(schedule.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(schedule.getActionName()).thenReturn(VMScheduleAction.START.name());
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
|
||||
Date actualScheduledTime = vmScheduleWorker.scheduleNextJob(schedule, new Date());
|
||||
|
||||
Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleCurrentScheduleWithTimeZoneChecks() throws Exception {
|
||||
String cron = "30 5 * * *";
|
||||
Date now = DateUtils.setSeconds(new Date(), 0);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
ResourceScheduleVO schedule = Mockito.mock(ResourceScheduleVO.class);
|
||||
Mockito.when(schedule.getEnabled()).thenReturn(true);
|
||||
Mockito.when(schedule.getSchedule()).thenReturn(cron);
|
||||
Mockito.when(schedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("EST").toZoneId());
|
||||
Mockito.when(schedule.getStartDate()).thenReturn(DateUtils.addDays(now, -1));
|
||||
Mockito.when(schedule.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(schedule.getActionName()).thenReturn(VMScheduleAction.START.name());
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
|
||||
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now.toInstant(), schedule.getTimeZoneId());
|
||||
zonedDateTime = zonedDateTime.withHour(5).withMinute(30).withSecond(0).withNano(0);
|
||||
Date expectedScheduledTime = Date.from(zonedDateTime.toInstant());
|
||||
if (expectedScheduledTime.before(now)) {
|
||||
expectedScheduledTime = DateUtils.addDays(expectedScheduledTime, 1);
|
||||
}
|
||||
|
||||
Date actualScheduledTime = vmScheduleWorker.scheduleNextJob(schedule, new Date());
|
||||
Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleExpired() {
|
||||
Date now = DateUtils.setSeconds(new Date(), 0);
|
||||
ResourceScheduleVO schedule = Mockito.mock(ResourceScheduleVO.class);
|
||||
Mockito.when(schedule.getEnabled()).thenReturn(true);
|
||||
Mockito.when(schedule.getSchedule()).thenReturn("* * * * *");
|
||||
Mockito.when(schedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("UTC").toZoneId());
|
||||
Mockito.when(schedule.getStartDate()).thenReturn(DateUtils.addDays(now, -1));
|
||||
Mockito.when(schedule.getEndDate()).thenReturn(DateUtils.addMinutes(now, -5));
|
||||
Mockito.when(schedule.getResourceId()).thenReturn(1L);
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(Mockito.mock(UserVm.class));
|
||||
Date actualDate = vmScheduleWorker.scheduleNextJob(schedule, new Date());
|
||||
Assert.assertNull(actualDate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleDisabled() {
|
||||
ResourceScheduleVO schedule = Mockito.mock(ResourceScheduleVO.class);
|
||||
Mockito.when(schedule.getEnabled()).thenReturn(false);
|
||||
Date actualDate = vmScheduleWorker.scheduleNextJob(schedule, new Date());
|
||||
Assert.assertNull(actualDate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteJobs() {
|
||||
ResourceScheduledJobVO job1 = Mockito.mock(ResourceScheduledJobVO.class);
|
||||
ResourceScheduledJobVO job2 = Mockito.mock(ResourceScheduledJobVO.class);
|
||||
|
||||
Mockito.doReturn(null).when(vmScheduleWorker).processJob(job2);
|
||||
|
||||
Mockito.when(resourceScheduledJobDao.acquireInLockTable(job1.getId())).thenReturn(job1);
|
||||
Mockito.when(resourceScheduledJobDao.acquireInLockTable(job2.getId())).thenReturn(job2);
|
||||
|
||||
Map<Long, ResourceScheduledJobVO> jobs = new HashMap<>();
|
||||
jobs.put(1L, job1);
|
||||
jobs.put(2L, job2);
|
||||
|
||||
ReflectionTestUtils.setField(vmScheduleWorker, "currentTimestamp", new Date());
|
||||
|
||||
vmScheduleWorker.executeJobs(jobs);
|
||||
|
||||
Mockito.verify(vmScheduleWorker, Mockito.times(2)).processJob(Mockito.any());
|
||||
Mockito.verify(resourceScheduledJobDao, Mockito.times(2)).acquireInLockTable(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubmitStopVMJob() {
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
Mockito.when(asyncJobManager.submitAsyncJob(Mockito.any(AsyncJobVO.class))).thenReturn(1L);
|
||||
Mockito.when(asyncJobDispatcher.getName()).thenReturn("ApiAsyncJobDispatcher");
|
||||
try (MockedStatic<ComponentContext> ignored = Mockito.mockStatic(ComponentContext.class)) {
|
||||
when(ComponentContext.inject(Mockito.any(StopVMCmd.class))).thenReturn(Mockito.mock(StopVMCmd.class));
|
||||
long jobId = vmScheduleWorker.submitAsyncJob(StopVMCmd.class, vm.getAccountId(), vm.getId(), 1L,
|
||||
Map.of("forced", "false"));
|
||||
Assert.assertEquals(1L, jobId);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubmitRebootVMJob() {
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
Mockito.when(asyncJobManager.submitAsyncJob(Mockito.any(AsyncJobVO.class))).thenReturn(1L);
|
||||
Mockito.when(asyncJobDispatcher.getName()).thenReturn("ApiAsyncJobDispatcher");
|
||||
try (MockedStatic<ComponentContext> ignored = Mockito.mockStatic(ComponentContext.class)) {
|
||||
when(ComponentContext.inject(Mockito.any(RebootVMCmd.class))).thenReturn(Mockito.mock(RebootVMCmd.class));
|
||||
long jobId = vmScheduleWorker.submitAsyncJob(RebootVMCmd.class, vm.getAccountId(), vm.getId(), 1L,
|
||||
Map.of("forced", "false"));
|
||||
Assert.assertEquals(1L, jobId);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubmitStartVMJob() {
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
Mockito.when(asyncJobManager.submitAsyncJob(Mockito.any(AsyncJobVO.class))).thenReturn(1L);
|
||||
Mockito.when(asyncJobDispatcher.getName()).thenReturn("ApiAsyncJobDispatcher");
|
||||
try (MockedStatic<ComponentContext> ignored = Mockito.mockStatic(ComponentContext.class)) {
|
||||
when(ComponentContext.inject(Mockito.any(StartVMCmd.class))).thenReturn(Mockito.mock(StartVMCmd.class));
|
||||
long jobId = vmScheduleWorker.submitAsyncJob(StartVMCmd.class, vm.getAccountId(), vm.getId(), 1L,
|
||||
Map.of());
|
||||
Assert.assertEquals(1L, jobId);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseActionResolvesEnum() {
|
||||
Assert.assertEquals(VMScheduleAction.START, vmScheduleWorker.parseAction("start"));
|
||||
Assert.assertEquals(VMScheduleAction.STOP, vmScheduleWorker.parseAction("STOP"));
|
||||
Assert.assertEquals(VMScheduleAction.FORCE_REBOOT, vmScheduleWorker.parseAction("Force_Reboot"));
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void parseActionThrowsOnUnknown() {
|
||||
vmScheduleWorker.parseAction("BOGUS");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,282 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.api.command.user.vm.CreateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.DeleteVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.ListVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.UpdateVMScheduleCmd;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.api.response.VMScheduleResponse;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.vm.schedule.dao.VMScheduleDao;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class VMScheduleManagerImplTest {
|
||||
|
||||
@Spy
|
||||
@InjectMocks
|
||||
VMScheduleManagerImpl vmScheduleManager = new VMScheduleManagerImpl();
|
||||
|
||||
@Mock
|
||||
VMScheduleDao vmScheduleDao;
|
||||
|
||||
@Mock
|
||||
VMScheduler vmScheduler;
|
||||
|
||||
@Mock
|
||||
UserVmManager userVmManager;
|
||||
|
||||
@Mock
|
||||
AccountManager accountManager;
|
||||
|
||||
private AutoCloseable closeable;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
closeable = MockitoAnnotations.openMocks(this);
|
||||
Account callingAccount = Mockito.mock(Account.class);
|
||||
User callingUser = Mockito.mock(User.class);
|
||||
CallContext.register(callingUser, callingAccount);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
closeable.close();
|
||||
}
|
||||
|
||||
private void validateResponse(VMScheduleResponse response, VMSchedule vmSchedule, VirtualMachine vm) {
|
||||
assertNotNull(response);
|
||||
Assert.assertEquals(ReflectionTestUtils.getField(response, "id"), vmSchedule.getUuid());
|
||||
Assert.assertEquals(ReflectionTestUtils.getField(response, "vmId"), vm.getUuid());
|
||||
Assert.assertEquals(ReflectionTestUtils.getField(response, "schedule"), vmSchedule.getSchedule());
|
||||
Assert.assertEquals(ReflectionTestUtils.getField(response, "timeZone"), vmSchedule.getTimeZone());
|
||||
Assert.assertEquals(ReflectionTestUtils.getField(response, "action"), vmSchedule.getAction());
|
||||
Assert.assertEquals(ReflectionTestUtils.getField(response, "startDate"), vmSchedule.getStartDate());
|
||||
Assert.assertEquals(ReflectionTestUtils.getField(response, "endDate"), vmSchedule.getEndDate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createSchedule() {
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
|
||||
CreateVMScheduleCmd cmd = Mockito.mock(CreateVMScheduleCmd.class);
|
||||
|
||||
Mockito.when(cmd.getVmId()).thenReturn(1L);
|
||||
Mockito.when(cmd.getSchedule()).thenReturn("0 0 * * *");
|
||||
Mockito.when(cmd.getTimeZone()).thenReturn("UTC");
|
||||
Mockito.when(cmd.getAction()).thenReturn("start");
|
||||
Mockito.when(cmd.getStartDate()).thenReturn(DateUtils.addDays(new Date(), 1));
|
||||
Mockito.when(cmd.getEndDate()).thenReturn(DateUtils.addDays(new Date(), 2));
|
||||
Mockito.when(vm.getUuid()).thenReturn(UUID.randomUUID().toString());
|
||||
Mockito.when(vmScheduleDao.persist(Mockito.any(VMScheduleVO.class))).thenReturn(vmSchedule);
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
|
||||
Mockito.doNothing().when(accountManager).checkAccess(Mockito.any(Account.class), Mockito.isNull(), Mockito.eq(false), Mockito.eq(vm));
|
||||
VMScheduleResponse response = vmScheduleManager.createSchedule(cmd);
|
||||
Mockito.verify(vmScheduleDao, Mockito.times(1)).persist(Mockito.any(VMScheduleVO.class));
|
||||
|
||||
validateResponse(response, vmSchedule, vm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createResponse() {
|
||||
VMSchedule vmSchedule = Mockito.mock(VMSchedule.class);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
Mockito.when(vmSchedule.getVmId()).thenReturn(1L);
|
||||
Mockito.when(userVmManager.getUserVm(vmSchedule.getVmId())).thenReturn(vm);
|
||||
|
||||
VMScheduleResponse response = vmScheduleManager.createResponse(vmSchedule);
|
||||
validateResponse(response, vmSchedule, vm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listSchedule() {
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
VMScheduleVO vmSchedule1 = Mockito.mock(VMScheduleVO.class);
|
||||
VMScheduleVO vmSchedule2 = Mockito.mock(VMScheduleVO.class);
|
||||
List<VMScheduleVO> vmScheduleList = new ArrayList<>();
|
||||
vmScheduleList.add(vmSchedule1);
|
||||
vmScheduleList.add(vmSchedule2);
|
||||
|
||||
Mockito.when(vm.getUuid()).thenReturn(UUID.randomUUID().toString());
|
||||
Mockito.when(userVmManager.getUserVm(1L)).thenReturn(vm);
|
||||
Mockito.when(
|
||||
vmScheduleDao.searchAndCount(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(),
|
||||
Mockito.anyBoolean(), Mockito.anyLong(), Mockito.anyLong())
|
||||
).thenReturn(new Pair<>(vmScheduleList, vmScheduleList.size()));
|
||||
Mockito.when(vmSchedule1.getVmId()).thenReturn(1L);
|
||||
Mockito.when(vmSchedule2.getVmId()).thenReturn(1L);
|
||||
|
||||
ListVMScheduleCmd cmd = Mockito.mock(ListVMScheduleCmd.class);
|
||||
Mockito.when(cmd.getVmId()).thenReturn(1L);
|
||||
|
||||
ListResponse<VMScheduleResponse> responseList = vmScheduleManager.listSchedule(cmd);
|
||||
Mockito.verify(vmScheduleDao, Mockito.times(1)).searchAndCount(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(),
|
||||
Mockito.anyBoolean(), Mockito.anyLong(), Mockito.anyLong());
|
||||
assertNotNull(responseList);
|
||||
Assert.assertEquals(2, (int) responseList.getCount());
|
||||
Assert.assertEquals(2, responseList.getResponses().size());
|
||||
|
||||
for (int i = 0; i < responseList.getResponses().size(); i++) {
|
||||
VMScheduleResponse response = responseList.getResponses().get(i);
|
||||
VMScheduleVO vmSchedule = vmScheduleList.get(i);
|
||||
validateResponse(response, vmSchedule, vm);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateSchedule() {
|
||||
VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
|
||||
UpdateVMScheduleCmd cmd = Mockito.mock(UpdateVMScheduleCmd.class);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
Mockito.when(cmd.getId()).thenReturn(1L);
|
||||
Mockito.when(cmd.getSchedule()).thenReturn("0 0 * * *");
|
||||
Mockito.when(cmd.getTimeZone()).thenReturn("UTC");
|
||||
Mockito.when(cmd.getStartDate()).thenReturn(DateUtils.addDays(new Date(), 1));
|
||||
Mockito.when(cmd.getEndDate()).thenReturn(DateUtils.addDays(new Date(), 2));
|
||||
Mockito.when(vmScheduleDao.findById(Mockito.anyLong())).thenReturn(vmSchedule);
|
||||
Mockito.when(vmScheduleDao.update(Mockito.eq(cmd.getId()), Mockito.any(VMScheduleVO.class))).thenReturn(true);
|
||||
Mockito.when(vmSchedule.getVmId()).thenReturn(1L);
|
||||
Mockito.when(vmSchedule.getStartDate()).thenReturn(DateUtils.addDays(new Date(), 1));
|
||||
Mockito.when(userVmManager.getUserVm(vmSchedule.getVmId())).thenReturn(vm);
|
||||
|
||||
VMScheduleResponse response = vmScheduleManager.updateSchedule(cmd);
|
||||
Mockito.verify(vmScheduleDao, Mockito.times(1)).update(Mockito.eq(cmd.getId()), Mockito.any(VMScheduleVO.class));
|
||||
|
||||
validateResponse(response, vmSchedule, vm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeScheduleByVmId() {
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
VMScheduleVO vmSchedule1 = Mockito.mock(VMScheduleVO.class);
|
||||
VMScheduleVO vmSchedule2 = Mockito.mock(VMScheduleVO.class);
|
||||
List<VMScheduleVO> vmScheduleList = new ArrayList<>();
|
||||
vmScheduleList.add(vmSchedule1);
|
||||
vmScheduleList.add(vmSchedule2);
|
||||
SearchCriteria<VMScheduleVO> sc = Mockito.mock(SearchCriteria.class);
|
||||
|
||||
Mockito.when(vm.getId()).thenReturn(1L);
|
||||
Mockito.when(vmScheduleDao.getSearchCriteriaForVMId(vm.getId())).thenReturn(sc);
|
||||
Mockito.when(vmScheduleDao.search(sc, null)).thenReturn(vmScheduleList);
|
||||
Mockito.when(vmSchedule1.getId()).thenReturn(1L);
|
||||
Mockito.when(vmSchedule2.getId()).thenReturn(2L);
|
||||
Mockito.when(vmScheduleDao.remove(sc)).thenReturn(2);
|
||||
|
||||
long rowsRemoved = vmScheduleManager.removeScheduleByVmId(vm.getId(), false);
|
||||
|
||||
Mockito.verify(vmScheduleDao, Mockito.times(1)).remove(sc);
|
||||
Assert.assertEquals(2, rowsRemoved);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeSchedule() {
|
||||
VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
DeleteVMScheduleCmd cmd = Mockito.mock(DeleteVMScheduleCmd.class);
|
||||
|
||||
Mockito.when(cmd.getId()).thenReturn(1L);
|
||||
Mockito.when(cmd.getVmId()).thenReturn(1L);
|
||||
Mockito.when(vmSchedule.getVmId()).thenReturn(1L);
|
||||
Mockito.when(userVmManager.getUserVm(cmd.getVmId())).thenReturn(vm);
|
||||
Mockito.when(vmScheduleDao.findById(Mockito.anyLong())).thenReturn(vmSchedule);
|
||||
Mockito.when(vmScheduleDao.removeSchedulesForVmIdAndIds(Mockito.anyLong(), Mockito.anyList())).thenReturn(1L);
|
||||
|
||||
Long rowsRemoved = vmScheduleManager.removeSchedule(cmd);
|
||||
|
||||
Mockito.verify(vmScheduleDao, Mockito.times(1)).removeSchedulesForVmIdAndIds(Mockito.anyLong(), Mockito.anyList());
|
||||
Assert.assertEquals(1L, (long) rowsRemoved);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateStartDateEndDate() {
|
||||
// Valid scenario 1
|
||||
// Start date is before end date
|
||||
Date startDate = DateUtils.addDays(new Date(), 1);
|
||||
Date endDate = DateUtils.addDays(new Date(), 2);
|
||||
vmScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
|
||||
|
||||
// Valid Scenario 2
|
||||
// Start date is before current date and end date is null
|
||||
endDate = null;
|
||||
vmScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
|
||||
|
||||
// Invalid Scenario 2
|
||||
// Start date is before current date
|
||||
startDate = DateUtils.addDays(new Date(), -1);
|
||||
try {
|
||||
vmScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
|
||||
Assert.fail("Should have thrown InvalidParameterValueException");
|
||||
} catch (InvalidParameterValueException e) {
|
||||
Assert.assertTrue(e.getMessage().contains("Invalid value for start date. Start date") &&
|
||||
e.getMessage().contains("can't be before current time"));
|
||||
}
|
||||
|
||||
// Invalid Scenario 2
|
||||
// Start date is after end date
|
||||
startDate = DateUtils.addDays(new Date(), 2);
|
||||
endDate = DateUtils.addDays(new Date(), 1);
|
||||
try {
|
||||
vmScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
|
||||
Assert.fail("Should have thrown InvalidParameterValueException");
|
||||
} catch (InvalidParameterValueException e) {
|
||||
Assert.assertTrue(e.getMessage().contains("Invalid value for end date. End date") &&
|
||||
e.getMessage().contains("can't be before start date"));
|
||||
}
|
||||
|
||||
// Invalid Scenario 3
|
||||
// End date is before current date
|
||||
startDate = DateUtils.addDays(new Date(), 1);
|
||||
endDate = DateUtils.addDays(new Date(), -1);
|
||||
try {
|
||||
vmScheduleManager.validateStartDateEndDate(startDate, endDate, TimeZone.getDefault());
|
||||
Assert.fail("Should have thrown InvalidParameterValueException");
|
||||
} catch (InvalidParameterValueException e) {
|
||||
Assert.assertTrue(e.getMessage().contains("Invalid value for end date. End date") &&
|
||||
e.getMessage().contains("can't be before current time"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,388 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cloudstack.vm.schedule;
|
||||
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.uservm.UserVm;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
|
||||
import org.apache.cloudstack.api.command.user.vm.StopVMCmd;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
||||
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
|
||||
import org.apache.cloudstack.vm.schedule.dao.VMScheduleDao;
|
||||
import org.apache.cloudstack.vm.schedule.dao.VMScheduledJobDao;
|
||||
import org.apache.commons.lang.time.DateUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import javax.persistence.EntityExistsException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class VMSchedulerImplTest {
|
||||
@Spy
|
||||
@InjectMocks
|
||||
private VMSchedulerImpl vmScheduler = new VMSchedulerImpl();
|
||||
|
||||
@Mock
|
||||
private UserVmManager userVmManager;
|
||||
|
||||
@Mock
|
||||
private VMScheduleDao vmScheduleDao;
|
||||
|
||||
@Mock
|
||||
private VMScheduledJobDao vmScheduledJobDao;
|
||||
|
||||
@Mock
|
||||
private EnumMap<VMSchedule.Action, String> actionEventMap;
|
||||
|
||||
@Mock
|
||||
private AsyncJobManager asyncJobManager;
|
||||
|
||||
@Mock
|
||||
private AsyncJobDispatcher asyncJobDispatcher;
|
||||
|
||||
private AutoCloseable closeable;
|
||||
|
||||
private MockedStatic<ActionEventUtils> actionEventUtilsMocked;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
closeable = MockitoAnnotations.openMocks(this);
|
||||
actionEventUtilsMocked = Mockito.mockStatic(ActionEventUtils.class);
|
||||
Mockito.when(ActionEventUtils.onScheduledActionEvent(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(),
|
||||
Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyBoolean(),
|
||||
Mockito.anyLong()))
|
||||
.thenReturn(1L);
|
||||
Mockito.when(ActionEventUtils.onCompletedActionEvent(Mockito.anyLong(), Mockito.anyLong(), Mockito.any(),
|
||||
Mockito.anyString(), Mockito.anyBoolean(),
|
||||
Mockito.anyString(),
|
||||
Mockito.anyLong(), Mockito.anyString(), Mockito.anyLong())).thenReturn(1L);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
actionEventUtilsMocked.close();
|
||||
closeable.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessJobRunning() {
|
||||
executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMSchedule.Action.STOP);
|
||||
executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMSchedule.Action.FORCE_STOP);
|
||||
executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMSchedule.Action.REBOOT);
|
||||
executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Running, VMSchedule.Action.FORCE_REBOOT);
|
||||
executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State.Stopped, VMSchedule.Action.START);
|
||||
}
|
||||
|
||||
private void executeProcessJobWithVMStateAndActionNonSkipped(VirtualMachine.State state, VMSchedule.Action action) {
|
||||
VMScheduledJob vmScheduledJob = Mockito.mock(VMScheduledJob.class);
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
|
||||
Long expectedValue = 1L;
|
||||
|
||||
prepareMocksForProcessJob(vm, vmScheduledJob, state, action, expectedValue);
|
||||
|
||||
Long jobId = vmScheduler.processJob(vmScheduledJob, vm);
|
||||
|
||||
actionEventUtilsMocked.verify(() -> ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, vm.getAccountId(), null,
|
||||
actionEventMap.get(action), true,
|
||||
String.format("Executing action (%s) for VM: %s", vmScheduledJob.getAction(), vm),
|
||||
vm.getId(), ApiCommandResourceType.VirtualMachine.toString(), 0));
|
||||
Assert.assertEquals(expectedValue, jobId);
|
||||
}
|
||||
|
||||
private void prepareMocksForProcessJob(VirtualMachine vm, VMScheduledJob vmScheduledJob,
|
||||
VirtualMachine.State vmState, VMSchedule.Action action,
|
||||
Long executeJobReturnValue) {
|
||||
Mockito.when(vm.getState()).thenReturn(vmState);
|
||||
Mockito.when(vmScheduledJob.getAction()).thenReturn(action);
|
||||
|
||||
if (executeJobReturnValue != null) {
|
||||
Mockito.doReturn(executeJobReturnValue).when(vmScheduler).executeStartVMJob(
|
||||
Mockito.any(VirtualMachine.class), Mockito.anyLong());
|
||||
Mockito.doReturn(executeJobReturnValue).when(vmScheduler).executeStopVMJob(
|
||||
Mockito.any(VirtualMachine.class), Mockito.anyBoolean(), Mockito.anyLong());
|
||||
Mockito.doReturn(executeJobReturnValue).when(vmScheduler).executeRebootVMJob(
|
||||
Mockito.any(VirtualMachine.class), Mockito.anyBoolean(), Mockito.anyLong());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessJobInvalidAction() {
|
||||
VMScheduledJob vmScheduledJob = Mockito.mock(VMScheduledJob.class);
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
|
||||
prepareMocksForProcessJob(vm, vmScheduledJob, VirtualMachine.State.Running, VMSchedule.Action.START, null);
|
||||
|
||||
Long jobId = vmScheduler.processJob(vmScheduledJob, vm);
|
||||
|
||||
Assert.assertNull(jobId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessJobVMInInvalidState() {
|
||||
VMScheduledJob vmScheduledJob = Mockito.mock(VMScheduledJob.class);
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
|
||||
prepareMocksForProcessJob(vm, vmScheduledJob, VirtualMachine.State.Unknown, VMSchedule.Action.START, null);
|
||||
|
||||
Long jobId = vmScheduler.processJob(vmScheduledJob, vm);
|
||||
|
||||
Assert.assertNull(jobId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleScheduleExists() {
|
||||
Date now = DateUtils.setSeconds(new Date(), 0);
|
||||
Date startDate = DateUtils.addDays(now, 1);
|
||||
Date expectedScheduledTime = DateUtils.round(DateUtils.addMinutes(startDate, 1), Calendar.MINUTE);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
|
||||
Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
|
||||
Mockito.when(vmSchedule.getSchedule()).thenReturn("* * * * *");
|
||||
Mockito.when(vmSchedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("UTC").toZoneId());
|
||||
Mockito.when(vmSchedule.getStartDate()).thenReturn(startDate);
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
|
||||
Mockito.when(vmScheduledJobDao.persist(Mockito.any())).thenThrow(EntityExistsException.class);
|
||||
Date actualScheduledTime = vmScheduler.scheduleNextJob(vmSchedule, new Date());
|
||||
|
||||
Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleFutureSchedule() {
|
||||
Date now = DateUtils.setSeconds(new Date(), 0);
|
||||
Date startDate = DateUtils.addDays(now, 1);
|
||||
Date expectedScheduledTime = DateUtils.round(DateUtils.addMinutes(startDate, 1), Calendar.MINUTE);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
|
||||
Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
|
||||
Mockito.when(vmSchedule.getSchedule()).thenReturn("* * * * *");
|
||||
Mockito.when(vmSchedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("UTC").toZoneId());
|
||||
Mockito.when(vmSchedule.getStartDate()).thenReturn(startDate);
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
|
||||
Date actualScheduledTime = vmScheduler.scheduleNextJob(vmSchedule, new Date());
|
||||
|
||||
Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleFutureScheduleWithTimeZoneChecks() throws Exception {
|
||||
// Ensure that Date vmSchedulerImpl.scheduleNextJob(VMScheduleVO vmSchedule) generates
|
||||
// the correct scheduled time on basis of schedule(cron), start date
|
||||
// and the timezone of the user. The system running the test can have any timezone.
|
||||
String cron = "30 5 * * *";
|
||||
|
||||
Date now = DateUtils.setSeconds(new Date(), 0);
|
||||
Date startDate = DateUtils.addDays(now, 1);
|
||||
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
|
||||
Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
|
||||
Mockito.when(vmSchedule.getSchedule()).thenReturn(cron);
|
||||
Mockito.when(vmSchedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("EST").toZoneId());
|
||||
Mockito.when(vmSchedule.getStartDate()).thenReturn(startDate);
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
|
||||
|
||||
// The timezone of the user is EST. The cron expression is 30 5 * * *.
|
||||
// The start date is 1 day from now. The expected scheduled time is 5:30 AM EST.
|
||||
// The actual scheduled time is 10:30 AM UTC.
|
||||
// The actual scheduled time is 5:30 AM EST.
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(startDate.toInstant(), vmSchedule.getTimeZoneId());
|
||||
zonedDateTime = zonedDateTime.withHour(5).withMinute(30).withSecond(0).withNano(0);
|
||||
Date expectedScheduledTime = Date.from(zonedDateTime.toInstant());
|
||||
|
||||
if (expectedScheduledTime.before(startDate)) {
|
||||
expectedScheduledTime = Date.from(zonedDateTime.plusDays(1).toInstant());
|
||||
}
|
||||
|
||||
Date actualScheduledTime = vmScheduler.scheduleNextJob(vmSchedule, new Date());
|
||||
Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleCurrentSchedule() {
|
||||
Date now = DateUtils.setSeconds(new Date(), 0);
|
||||
Date expectedScheduledTime = DateUtils.round(DateUtils.addMinutes(now, 1), Calendar.MINUTE);
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
|
||||
Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
|
||||
Mockito.when(vmSchedule.getSchedule()).thenReturn("* * * * *");
|
||||
Mockito.when(vmSchedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("UTC").toZoneId());
|
||||
Mockito.when(vmSchedule.getStartDate()).thenReturn(DateUtils.addDays(now, -1));
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
|
||||
Date actualScheduledTime = vmScheduler.scheduleNextJob(vmSchedule, new Date());
|
||||
|
||||
Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleCurrentScheduleWithTimeZoneChecks() throws Exception {
|
||||
// Ensure that Date vmSchedulerImpl.scheduleNextJob(VMScheduleVO vmSchedule) generates
|
||||
// the correct scheduled time on basis of schedule(cron), start date
|
||||
// and the timezone of the user. The system running the test can have any timezone.
|
||||
String cron = "30 5 * * *";
|
||||
|
||||
Date now = DateUtils.setSeconds(new Date(), 0);
|
||||
|
||||
UserVm vm = Mockito.mock(UserVm.class);
|
||||
|
||||
VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
|
||||
Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
|
||||
Mockito.when(vmSchedule.getSchedule()).thenReturn(cron);
|
||||
Mockito.when(vmSchedule.getTimeZoneId()).thenReturn(TimeZone.getTimeZone("EST").toZoneId());
|
||||
Mockito.when(vmSchedule.getStartDate()).thenReturn(DateUtils.addDays(now, -1));
|
||||
Mockito.when(userVmManager.getUserVm(Mockito.anyLong())).thenReturn(vm);
|
||||
|
||||
// The timezone of the user is EST. The cron expression is 30 5 * * *.
|
||||
// The start date is 1 day ago. The expected scheduled time is 5:30 AM EST.
|
||||
// The actual scheduled time is 10:30 AM UTC.
|
||||
// The actual scheduled time is 5:30 AM EST.
|
||||
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now.toInstant(), vmSchedule.getTimeZoneId());
|
||||
zonedDateTime = zonedDateTime.withHour(5).withMinute(30).withSecond(0).withNano(0);
|
||||
Date expectedScheduledTime = Date.from(zonedDateTime.toInstant());
|
||||
|
||||
if (expectedScheduledTime.before(now)) {
|
||||
expectedScheduledTime = DateUtils.addDays(expectedScheduledTime, 1);
|
||||
}
|
||||
|
||||
Date actualScheduledTime = vmScheduler.scheduleNextJob(vmSchedule, new Date());
|
||||
Assert.assertEquals(expectedScheduledTime, actualScheduledTime);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleExpired() {
|
||||
VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
|
||||
Mockito.when(vmSchedule.getEndDate()).thenReturn(DateUtils.addMinutes(new Date(), -5));
|
||||
Mockito.when(vmSchedule.getEnabled()).thenReturn(true);
|
||||
Date actualDate = vmScheduler.scheduleNextJob(vmSchedule, new Date());
|
||||
Assert.assertNull(actualDate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScheduleNextJobScheduleDisabled() {
|
||||
VMScheduleVO vmSchedule = Mockito.mock(VMScheduleVO.class);
|
||||
Mockito.when(vmSchedule.getEnabled()).thenReturn(false);
|
||||
Date actualDate = vmScheduler.scheduleNextJob(vmSchedule, new Date());
|
||||
Assert.assertNull(actualDate);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testExecuteJobs() {
|
||||
/*
|
||||
Test VMSchedulerImpl.executeJobs() method with a map of VMScheduledJob objects
|
||||
covering all the possible scenarios
|
||||
1. When the job is executed successfully
|
||||
2. When the job is skipped (processJob returns null)
|
||||
*/
|
||||
|
||||
VMScheduledJobVO job1 = Mockito.mock(VMScheduledJobVO.class);
|
||||
VMScheduledJobVO job2 = Mockito.mock(VMScheduledJobVO.class);
|
||||
|
||||
UserVm vm1 = Mockito.mock(UserVm.class);
|
||||
UserVm vm2 = Mockito.mock(UserVm.class);
|
||||
|
||||
Mockito.when(job2.getVmId()).thenReturn(2L);
|
||||
|
||||
Mockito.when(userVmManager.getUserVm(2L)).thenReturn(vm2);
|
||||
|
||||
Mockito.doReturn(null).when(vmScheduler).processJob(job2, vm2);
|
||||
|
||||
Mockito.when(vmScheduledJobDao.acquireInLockTable(job1.getId())).thenReturn(job1);
|
||||
Mockito.when(vmScheduledJobDao.acquireInLockTable(job2.getId())).thenReturn(job2);
|
||||
|
||||
Map<Long, VMScheduledJob> jobs = new HashMap<>();
|
||||
jobs.put(1L, job1);
|
||||
jobs.put(2L, job2);
|
||||
|
||||
ReflectionTestUtils.setField(vmScheduler, "currentTimestamp", new Date());
|
||||
|
||||
vmScheduler.executeJobs(jobs);
|
||||
|
||||
Mockito.verify(vmScheduler, Mockito.times(2)).processJob(Mockito.any(), Mockito.any());
|
||||
Mockito.verify(vmScheduledJobDao, Mockito.times(2)).acquireInLockTable(Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteStopVMJob() {
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
Mockito.when(asyncJobManager.submitAsyncJob(Mockito.any(AsyncJobVO.class))).thenReturn(1L);
|
||||
try (MockedStatic<ComponentContext> ignored = Mockito.mockStatic(ComponentContext.class)) {
|
||||
when(ComponentContext.inject(StopVMCmd.class)).thenReturn(Mockito.mock(StopVMCmd.class));
|
||||
long jobId = vmScheduler.executeStopVMJob(vm, false, 1L);
|
||||
|
||||
Assert.assertEquals(1L, jobId);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteRebootVMJob() {
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
Mockito.when(asyncJobManager.submitAsyncJob(Mockito.any(AsyncJobVO.class))).thenReturn(1L);
|
||||
try (MockedStatic<ComponentContext> ignored = Mockito.mockStatic(ComponentContext.class)) {
|
||||
when(ComponentContext.inject(RebootVMCmd.class)).thenReturn(Mockito.mock(RebootVMCmd.class));
|
||||
long jobId = vmScheduler.executeRebootVMJob(vm, false, 1L);
|
||||
|
||||
Assert.assertEquals(1L, jobId);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteStartVMJob() {
|
||||
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
|
||||
Mockito.when(asyncJobManager.submitAsyncJob(Mockito.any(AsyncJobVO.class))).thenReturn(1L);
|
||||
try (MockedStatic<ComponentContext> ignored = Mockito.mockStatic(ComponentContext.class)) {
|
||||
when(ComponentContext.inject(StartVMCmd.class)).thenReturn(Mockito.mock(StartVMCmd.class));
|
||||
long jobId = vmScheduler.executeStartVMJob(vm, 1L);
|
||||
|
||||
Assert.assertEquals(1L, jobId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
""" P1 tests for VM Schedule
|
||||
"""
|
||||
from marvin.cloudstackTestCase import cloudstackTestCase
|
||||
from marvin.lib.base import Account, ServiceOffering, VirtualMachine, VMSchedule
|
||||
from marvin.lib.base import Account, ServiceOffering, VirtualMachine, ResourceSchedule
|
||||
from marvin.lib.common import get_domain, get_zone, get_template
|
||||
from marvin.lib.utils import cleanup_resources
|
||||
|
||||
|
|
@ -28,6 +28,8 @@ import datetime
|
|||
import time
|
||||
|
||||
|
||||
RESOURCE_TYPE = "VirtualMachine"
|
||||
|
||||
class Services:
|
||||
"""Test Snapshots Services"""
|
||||
|
||||
|
|
@ -148,8 +150,9 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
|
||||
# Create VM Schedule
|
||||
schedule = "0 0 1 * *"
|
||||
vmschedule = VMSchedule.create(
|
||||
vmschedule = ResourceSchedule.create(
|
||||
self.apiclient,
|
||||
RESOURCE_TYPE,
|
||||
self.virtual_machine.id,
|
||||
"start",
|
||||
schedule,
|
||||
|
|
@ -166,8 +169,8 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
self.debug("Created VM Schedule with ID: %s" % vmschedule.id)
|
||||
|
||||
# List VM Schedule
|
||||
vmschedules = VMSchedule.list(
|
||||
self.apiclient, self.virtual_machine.id, id=vmschedule.id
|
||||
vmschedules = ResourceSchedule.list(
|
||||
self.apiclient, RESOURCE_TYPE, self.virtual_machine.id, id=vmschedule.id
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
|
|
@ -187,9 +190,15 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
)
|
||||
|
||||
self.assertEqual(
|
||||
vmschedules[0].virtualmachineid,
|
||||
vmschedules[0].resourceid,
|
||||
self.virtual_machine.id,
|
||||
"Check VM ID in list resources call",
|
||||
"Check resource ID in list resources call",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
vmschedules[0].resourcetype,
|
||||
RESOURCE_TYPE,
|
||||
"Check resource type in list resources call",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
|
|
@ -204,9 +213,9 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
"Check VM Schedule timezone in list resources call",
|
||||
)
|
||||
|
||||
# Check for entry in vm_scheduled_job in db
|
||||
# Check for entry in resource_scheduled_job in db
|
||||
vmscheduled_job = self.dbclient.execute(
|
||||
"select * from vm_scheduled_job where vm_schedule_id IN (SELECT id FROM vm_schedule WHERE uuid = '%s')"
|
||||
"select * from resource_scheduled_job where schedule_id IN (SELECT id FROM resource_schedule WHERE uuid = '%s')"
|
||||
% vmschedule.id,
|
||||
db="cloud",
|
||||
)
|
||||
|
|
@ -214,13 +223,13 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
self.assertIsInstance(
|
||||
vmscheduled_job,
|
||||
list,
|
||||
"Check if VM Schedule exists in vm_scheduled_job table",
|
||||
"Check if VM Schedule exists in resource_scheduled_job table",
|
||||
)
|
||||
|
||||
self.assertGreater(
|
||||
len(vmscheduled_job),
|
||||
0,
|
||||
"Check if VM Schedule exists in vm_scheduled_job table",
|
||||
"Check if VM Schedule exists in resource_scheduled_job table",
|
||||
)
|
||||
return
|
||||
|
||||
|
|
@ -237,8 +246,9 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
|
||||
# Create VM Schedule with invalid virtual machine ID
|
||||
with self.assertRaises(Exception):
|
||||
VMSchedule.create(
|
||||
ResourceSchedule.create(
|
||||
self.apiclient,
|
||||
RESOURCE_TYPE,
|
||||
"invalid",
|
||||
"start",
|
||||
"0 0 1 * *",
|
||||
|
|
@ -251,8 +261,9 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
|
||||
# Create VM Schedule with invalid schedule
|
||||
with self.assertRaises(Exception):
|
||||
VMSchedule.create(
|
||||
ResourceSchedule.create(
|
||||
self.apiclient,
|
||||
RESOURCE_TYPE,
|
||||
self.virtual_machine.id,
|
||||
"start",
|
||||
"invalid",
|
||||
|
|
@ -265,8 +276,9 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
|
||||
# Create VM Schedule with invalid start date
|
||||
with self.assertRaises(Exception):
|
||||
VMSchedule.create(
|
||||
ResourceSchedule.create(
|
||||
self.apiclient,
|
||||
RESOURCE_TYPE,
|
||||
self.virtual_machine.id,
|
||||
"start",
|
||||
"0 0 1 * *",
|
||||
|
|
@ -277,8 +289,9 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
|
||||
# Create VM Schedule with invalid action
|
||||
with self.assertRaises(Exception):
|
||||
VMSchedule.create(
|
||||
ResourceSchedule.create(
|
||||
self.apiclient,
|
||||
RESOURCE_TYPE,
|
||||
self.virtual_machine.id,
|
||||
"invalid",
|
||||
"0 0 1 * *",
|
||||
|
|
@ -291,8 +304,9 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
|
||||
# test invalid end date
|
||||
with self.assertRaises(Exception):
|
||||
VMSchedule.create(
|
||||
ResourceSchedule.create(
|
||||
self.apiclient,
|
||||
RESOURCE_TYPE,
|
||||
self.virtual_machine.id,
|
||||
"start",
|
||||
"0 0 1 * *",
|
||||
|
|
@ -315,8 +329,9 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
|
||||
# Create VM Schedule
|
||||
schedule = "0 0 1 * *"
|
||||
vmschedule = VMSchedule.create(
|
||||
vmschedule = ResourceSchedule.create(
|
||||
self.apiclient,
|
||||
RESOURCE_TYPE,
|
||||
self.virtual_machine.id,
|
||||
"start",
|
||||
schedule,
|
||||
|
|
@ -351,8 +366,8 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
self.debug("Updated VM Schedule with ID: %s" % vmschedule.id)
|
||||
|
||||
# List VM Schedule
|
||||
vmschedules = VMSchedule.list(
|
||||
self.apiclient, self.virtual_machine.id, id=vmschedule.id
|
||||
vmschedules = ResourceSchedule.list(
|
||||
self.apiclient, RESOURCE_TYPE, self.virtual_machine.id, id=vmschedule.id
|
||||
)
|
||||
self.assertEqual(
|
||||
isinstance(vmschedules, list),
|
||||
|
|
@ -371,11 +386,17 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
)
|
||||
|
||||
self.assertEqual(
|
||||
vmschedules[0].virtualmachineid,
|
||||
vmschedules[0].resourceid,
|
||||
self.virtual_machine.id,
|
||||
"Check VM ID in list resources call",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
vmschedules[0].resourcetype,
|
||||
RESOURCE_TYPE,
|
||||
"Check VM ID in list resources call",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
vmschedules[0].schedule,
|
||||
new_schedule,
|
||||
|
|
@ -395,8 +416,9 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
|
||||
# Create VM Schedule
|
||||
schedule = "0 0 1 * *"
|
||||
vmschedule = VMSchedule.create(
|
||||
vmschedule = ResourceSchedule.create(
|
||||
self.apiclient,
|
||||
RESOURCE_TYPE,
|
||||
self.virtual_machine.id,
|
||||
"start",
|
||||
schedule,
|
||||
|
|
@ -486,8 +508,9 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
|
||||
# Create VM Schedule - start
|
||||
start_schedule = "*/2 * * * *"
|
||||
start_vmschedule = VMSchedule.create(
|
||||
start_vmschedule = ResourceSchedule.create(
|
||||
self.apiclient,
|
||||
RESOURCE_TYPE,
|
||||
self.virtual_machine.id,
|
||||
"start",
|
||||
start_schedule,
|
||||
|
|
@ -503,8 +526,9 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
|
||||
# Create VM Schedule - stop
|
||||
stop_schedule = "*/1 * * * *"
|
||||
stop_vmschedule = VMSchedule.create(
|
||||
stop_vmschedule = ResourceSchedule.create(
|
||||
self.apiclient,
|
||||
RESOURCE_TYPE,
|
||||
self.virtual_machine.id,
|
||||
"stop",
|
||||
stop_schedule,
|
||||
|
|
@ -519,8 +543,8 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
self.debug("Created VM Schedule with ID: %s" % stop_vmschedule.id)
|
||||
|
||||
# Verify VM Schedule is created
|
||||
vmschedules = VMSchedule.list(
|
||||
self.apiclient, self.virtual_machine.id, id=start_vmschedule.id
|
||||
vmschedules = ResourceSchedule.list(
|
||||
self.apiclient, RESOURCE_TYPE, self.virtual_machine.id, id=start_vmschedule.id
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
|
|
@ -575,15 +599,15 @@ class TestVMSchedule(cloudstackTestCase):
|
|||
|
||||
# Verify VM Schedule is deleted
|
||||
self.assertEqual(
|
||||
VMSchedule.list(
|
||||
self.apiclient, self.virtual_machine.id, id=start_vmschedule.id
|
||||
ResourceSchedule.list(
|
||||
self.apiclient, RESOURCE_TYPE, self.virtual_machine.id, id=start_vmschedule.id
|
||||
),
|
||||
None,
|
||||
"Check VM Schedule is deleted",
|
||||
)
|
||||
self.assertEqual(
|
||||
VMSchedule.list(
|
||||
self.apiclient, self.virtual_machine.id, id=stop_vmschedule.id
|
||||
ResourceSchedule.list(
|
||||
self.apiclient, RESOURCE_TYPE, self.virtual_machine.id, id=stop_vmschedule.id
|
||||
),
|
||||
None,
|
||||
"Check VM Schedule is deleted",
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ known_categories = {
|
|||
'removeQuarantinedIp': 'IP Quarantine',
|
||||
'Shutdown': 'Maintenance',
|
||||
'Maintenance': 'Maintenance',
|
||||
'ResourceSchedule': 'Resource Schedule',
|
||||
'addObjectStoragePool': 'Object Store',
|
||||
'listObjectStoragePools': 'Object Store',
|
||||
'deleteObjectStoragePool': 'Object Store',
|
||||
|
|
|
|||
|
|
@ -7052,6 +7052,51 @@ class VMSchedule:
|
|||
cmd.virtualmachineid = self.virtualmachineid
|
||||
return (apiclient.deleteVMSchedule(cmd))
|
||||
|
||||
|
||||
class ResourceSchedule:
|
||||
|
||||
def __init__(self, items):
|
||||
self.__dict__.update(items)
|
||||
|
||||
@classmethod
|
||||
def create(cls, apiclient, resourcetype, resourceid, action, schedule, timezone, startdate, enddate=None, enabled=False, description=None, details=None):
|
||||
cmd = createResourceSchedule.createResourceScheduleCmd()
|
||||
cmd.resourcetype = resourcetype
|
||||
cmd.resourceid = resourceid
|
||||
cmd.description = description
|
||||
cmd.action = action
|
||||
cmd.schedule = schedule
|
||||
cmd.timezone = timezone
|
||||
cmd.startdate = startdate
|
||||
cmd.enddate = enddate
|
||||
cmd.enabled = enabled
|
||||
cmd.details = details
|
||||
return ResourceSchedule(apiclient.createResourceSchedule(cmd).__dict__)
|
||||
|
||||
@classmethod
|
||||
def list(cls, apiclient, resourcetype, resourceid, id=None, enabled=None, action=None):
|
||||
cmd = listResourceSchedule.listResourceScheduleCmd()
|
||||
cmd.resourcetype = resourcetype
|
||||
cmd.resourceid = resourceid
|
||||
cmd.id = id
|
||||
cmd.enabled = enabled
|
||||
cmd.action = action
|
||||
return apiclient.listResourceSchedule(cmd)
|
||||
|
||||
def update(self, apiclient, **kwargs):
|
||||
cmd = updateResourceSchedule.updateResourceScheduleCmd()
|
||||
cmd.id = self.id
|
||||
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||
return apiclient.updateResourceSchedule(cmd)
|
||||
|
||||
def delete(self, apiclient):
|
||||
cmd = deleteResourceSchedule.deleteResourceScheduleCmd()
|
||||
cmd.id = self.id
|
||||
cmd.resourcetype = self.resourcetype
|
||||
cmd.resourceid = self.resourceid
|
||||
return apiclient.deleteResourceSchedule(cmd)
|
||||
|
||||
|
||||
class VnfTemplate:
|
||||
"""Manage VNF template life cycle"""
|
||||
|
||||
|
|
|
|||
|
|
@ -2780,7 +2780,8 @@
|
|||
"label.vmlimit": "Instance limits",
|
||||
"label.vmname": "Instance name",
|
||||
"label.vms": "Instances",
|
||||
"label.vmscheduleactions": "Actions",
|
||||
"label.scheduleactions": "Actions",
|
||||
|
||||
"label.vmstate": "Instance state",
|
||||
"label.vmtotal": "Total of Instances",
|
||||
"label.vmware": "VMware",
|
||||
|
|
@ -3536,7 +3537,7 @@
|
|||
"message.error.remove.nic": "There was an error",
|
||||
"message.error.remove.secondary.ipaddress": "There was an error removing the secondary IP Address",
|
||||
"message.error.remove.tungsten.routing.policy": "Removing Tungsten-Fabric Routing Policy from Network failed",
|
||||
"message.error.remove.vm.schedule": "Removing Instance Schedule failed",
|
||||
"message.error.remove.resource.schedule": "Removing Schedule for the resource failed",
|
||||
"message.error.required.input": "Please enter input",
|
||||
"message.error.reset.config": "Unable to reset config to default value",
|
||||
"message.error.retrieve.kubeconfig": "Unable to retrieve Kubernetes Cluster config",
|
||||
|
|
@ -3931,7 +3932,7 @@
|
|||
"message.success.config.backup.schedule": "Successfully configured Instance backup schedule",
|
||||
"message.success.config.health.monitor": "Successfully Configure Health Monitor",
|
||||
"message.success.config.sticky.policy": "Successfully configured sticky policy",
|
||||
"message.success.config.vm.schedule": "Successfully configured Instance schedule",
|
||||
"message.success.config.resource.schedule": "Successfully configured schedule for the resource",
|
||||
"message.success.copy.clipboard": "Successfully copied to clipboard",
|
||||
"message.success.create.account": "Successfully created Account",
|
||||
"message.success.create.asnrange": "Successfully created AS Range",
|
||||
|
|
|
|||
|
|
@ -2466,7 +2466,7 @@
|
|||
"label.vmlimit": "ఉదాహరణ పరిమితులు",
|
||||
"label.vmname": "ఉదాహరణ పేరు",
|
||||
"label.vms": "సందర్భాలు",
|
||||
"label.vmscheduleactions": "చర్యలు",
|
||||
"label.scheduleactions": "చర్యలు",
|
||||
"label.vmstate": "ఉదాహరణ స్థితి",
|
||||
"label.vmtotal": "మొత్తం సందర్భాలు",
|
||||
"label.vmware": "VMware",
|
||||
|
|
@ -3107,7 +3107,7 @@
|
|||
"message.error.remove.nic": "లోపం ఏర్పడింది",
|
||||
"message.error.remove.secondary.ipaddress": "ద్వితీయ IP చిరునామాను తీసివేయడంలో లోపం ఏర్పడింది",
|
||||
"message.error.remove.tungsten.routing.policy": "నెట్వర్క్ నుండి టంగ్స్టన్-ఫాబ్రిక్ రూటింగ్ విధానాన్ని తీసివేయడం విఫలమైంది",
|
||||
"message.error.remove.vm.schedule": "ఉదాహరణ షెడ్యూల్ని తీసివేయడం విఫలమైంది",
|
||||
"message.error.remove.resource.schedule": "వనరు కోసం షెడ్యూల్ను తొలగించడం విఫలమైంది",
|
||||
"message.error.required.input": "దయచేసి ఇన్పుట్ని నమోదు చేయండి",
|
||||
"message.error.reset.config": "కాన్ఫిగర్ని డిఫాల్ట్ విలువకు రీసెట్ చేయడం సాధ్యపడలేదు",
|
||||
"message.error.retrieve.kubeconfig": "Kubernetes క్లస్టర్ కాన్ఫిగరేషన్ని తిరిగి పొందడం సాధ్యం కాలేదు",
|
||||
|
|
@ -3462,7 +3462,7 @@
|
|||
"message.success.config.backup.schedule": "ఉదాహరణ బ్యాకప్ షెడ్యూల్ విజయవంతంగా కాన్ఫిగర్ చేయబడింది",
|
||||
"message.success.config.health.monitor": "హెల్త్ మానిటర్ని విజయవంతంగా కాన్ఫిగర్ చేయండి",
|
||||
"message.success.config.sticky.policy": "స్టిక్కీ పాలసీ విజయవంతంగా కాన్ఫిగర్ చేయబడింది",
|
||||
"message.success.config.vm.schedule": "ఉదాహరణ షెడ్యూల్ విజయవంతంగా కాన్ఫిగర్ చేయబడింది",
|
||||
"message.success.config.resource.schedule": "వనరు కోసం షెడ్యూల్ విజయవంతంగా కాన్ఫిగర్ చేయబడింది",
|
||||
"message.success.copy.clipboard": "క్లిప్బోర్డ్కి విజయవంతంగా కాపీ చేయబడింది",
|
||||
"message.success.create.account": "ఖాతా విజయవంతంగా సృష్టించబడింది",
|
||||
"message.success.create.asnrange": "AS పరిధి విజయవంతంగా సృష్టించబడింది",
|
||||
|
|
|
|||
|
|
@ -1037,21 +1037,11 @@
|
|||
/>
|
||||
<slot></slot>
|
||||
</template>
|
||||
<template v-if="column.key === 'vmScheduleActions'">
|
||||
<tooltip-button
|
||||
:tooltip="$t('label.edit')"
|
||||
:disabled="!('updateVMSchedule' in $store.getters.apis)"
|
||||
icon="edit-outlined"
|
||||
@onClick="updateVMSchedule(record)"
|
||||
/>
|
||||
<tooltip-button
|
||||
:tooltip="$t('label.remove')"
|
||||
:disabled="!('deleteVMSchedule' in $store.getters.apis)"
|
||||
icon="delete-outlined"
|
||||
:danger="true"
|
||||
type="primary"
|
||||
@onClick="removeVMSchedule(record)"
|
||||
/>
|
||||
<template v-if="column.key === 'scheduleActions'">
|
||||
<slot
|
||||
name="scheduleActions"
|
||||
:record="record"
|
||||
></slot>
|
||||
</template>
|
||||
<template v-if="column.key === 'vgpuActions'">
|
||||
<slot name="actionButtons" :record="record" :actions="actions"></slot>
|
||||
|
|
@ -1397,12 +1387,6 @@ export default {
|
|||
editTariffValue (record) {
|
||||
this.$emit('edit-tariff-action', true, record)
|
||||
},
|
||||
updateVMSchedule (record) {
|
||||
this.$emit('update-vm-schedule', record)
|
||||
},
|
||||
removeVMSchedule (record) {
|
||||
this.$emit('remove-vm-schedule', record)
|
||||
},
|
||||
ipV6Address (text, record) {
|
||||
if (!record || !record.nic || record.nic.length === 0) {
|
||||
return ''
|
||||
|
|
|
|||
|
|
@ -87,9 +87,14 @@
|
|||
:routerlinks="(record) => { return { name: '/securitygroups/' + record.id } }"
|
||||
:showSearch="false"/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('label.schedules')" key="schedules" v-if="'listVMSchedule' in $store.getters.apis">
|
||||
<InstanceSchedules
|
||||
:virtualmachine="vm"
|
||||
<a-tab-pane
|
||||
:tab="$t('label.schedules')"
|
||||
key="schedules"
|
||||
v-if="'listResourceSchedule' in $store.getters.apis"
|
||||
>
|
||||
<ResourceSchedules
|
||||
:resource="vm"
|
||||
resourceType="VirtualMachine"
|
||||
:loading="loading"/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('label.settings')" key="settings">
|
||||
|
|
@ -145,7 +150,7 @@ import EventsTab from '@/components/view/EventsTab'
|
|||
import DetailSettings from '@/components/view/DetailSettings'
|
||||
import CreateVolume from '@/views/storage/CreateVolume'
|
||||
import NicsTab from '@/views/network/NicsTab'
|
||||
import InstanceSchedules from '@/views/compute/InstanceSchedules.vue'
|
||||
import ResourceSchedules from '@/views/compute/ResourceSchedules.vue'
|
||||
import ListResourceTable from '@/components/view/ListResourceTable'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
|
|
@ -165,7 +170,7 @@ export default {
|
|||
CreateVolume,
|
||||
NicsTab,
|
||||
GPUTab,
|
||||
InstanceSchedules,
|
||||
ResourceSchedules,
|
||||
ListResourceTable,
|
||||
SecurityGroupSelection,
|
||||
TooltipButton,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
style="width: 100%; margin-bottom: 10px"
|
||||
@click="showAddModal"
|
||||
:loading="loading"
|
||||
:disabled="!('createVMSchedule' in $store.getters.apis)"
|
||||
:disabled="!('createResourceSchedule' in $store.getters.apis)"
|
||||
>
|
||||
<template #icon><plus-outlined /></template> {{ $t('label.schedule.add') }}
|
||||
</a-button>
|
||||
|
|
@ -34,10 +34,25 @@
|
|||
:selectedColumns="selectedColumnKeys"
|
||||
ref="listview"
|
||||
@update-selected-columns="updateSelectedColumns"
|
||||
@update-vm-schedule="updateVMSchedule"
|
||||
@remove-vm-schedule="removeVMSchedule"
|
||||
@refresh="this.fetchData"
|
||||
/>
|
||||
>
|
||||
<template #scheduleActions="{ record }">
|
||||
<tooltip-button
|
||||
:tooltip="$t('label.edit')"
|
||||
:disabled="!('updateResourceSchedule' in $store.getters.apis)"
|
||||
icon="edit-outlined"
|
||||
@onClick="updateSchedule(record)"
|
||||
/>
|
||||
<tooltip-button
|
||||
:tooltip="$t('label.remove')"
|
||||
:disabled="!('deleteResourceSchedule' in $store.getters.apis)"
|
||||
icon="delete-outlined"
|
||||
:danger="true"
|
||||
type="primary"
|
||||
@onClick="removeSchedule(record)"
|
||||
/>
|
||||
</template>
|
||||
</list-view>
|
||||
<a-pagination
|
||||
class="row-element"
|
||||
style="margin-top: 10px"
|
||||
|
|
@ -256,6 +271,7 @@ import { reactive, ref, toRaw } from 'vue'
|
|||
import { getAPI, postAPI } from '@/api'
|
||||
import ListView from '@/components/view/ListView'
|
||||
import Status from '@/components/widgets/Status'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||
import { mixinForm } from '@/utils/mixin'
|
||||
import { timeZone } from '@/utils/timezone'
|
||||
|
|
@ -269,39 +285,46 @@ dayjs.extend(utc)
|
|||
dayjs.extend(timezone)
|
||||
|
||||
export default {
|
||||
name: 'InstanceSchedules',
|
||||
name: 'ResourceSchedules',
|
||||
mixins: [mixinForm],
|
||||
components: {
|
||||
Status,
|
||||
ListView,
|
||||
TooltipButton,
|
||||
TooltipLabel
|
||||
},
|
||||
props: {
|
||||
virtualmachine: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
resourceType: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
this.fetchTimeZone = debounce(this.fetchTimeZone, 800)
|
||||
return {
|
||||
tabLoading: false,
|
||||
columnKeys: ['action', 'enabled', 'description', 'schedule', 'timezone', 'startdate', 'enddate', 'created', 'vmScheduleActions'],
|
||||
columnKeys: ['action', 'enabled', 'description', 'schedule', 'timezone', 'startdate', 'enddate', 'created', 'scheduleActions'],
|
||||
selectedColumnKeys: [],
|
||||
columns: [],
|
||||
schedules: [],
|
||||
timeZoneMap: [],
|
||||
actions: [
|
||||
{ value: 'START', label: 'label.start' },
|
||||
{ value: 'STOP', label: 'label.stop' },
|
||||
{ value: 'REBOOT', label: 'label.reboot' },
|
||||
{ value: 'FORCE_STOP', label: 'label.force.stop' },
|
||||
{ value: 'FORCE_REBOOT', label: 'label.force.reboot' }
|
||||
],
|
||||
resourceActionsMap: {
|
||||
VirtualMachine: [
|
||||
{ value: 'START', label: 'label.start' },
|
||||
{ value: 'STOP', label: 'label.stop' },
|
||||
{ value: 'REBOOT', label: 'label.reboot' },
|
||||
{ value: 'FORCE_STOP', label: 'label.force.stop' },
|
||||
{ value: 'FORCE_REBOOT', label: 'label.force.reboot' }
|
||||
]
|
||||
},
|
||||
periods: [
|
||||
{ id: 'year', value: ['month', 'day', 'dayOfWeek', 'hour', 'minute'] },
|
||||
{ id: 'month', value: ['day', 'dayOfWeek', 'hour', 'minute'] },
|
||||
|
|
@ -319,7 +342,7 @@ export default {
|
|||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
this.apiParams = this.$getApiParams('createVMSchedule')
|
||||
this.apiParams = this.$getApiParams('createResourceSchedule')
|
||||
},
|
||||
computed: {
|
||||
pageSizeOptions () {
|
||||
|
|
@ -330,6 +353,9 @@ export default {
|
|||
return [...new Set(sizes)].sort(function (a, b) {
|
||||
return a - b
|
||||
}).map(String)
|
||||
},
|
||||
actions () {
|
||||
return this.resourceActionsMap[this.resourceType] || []
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
|
@ -341,7 +367,8 @@ export default {
|
|||
this.fetchTimeZone()
|
||||
},
|
||||
watch: {
|
||||
virtualmachine: {
|
||||
resource: {
|
||||
deep: true,
|
||||
handler () {
|
||||
this.fetchSchedules()
|
||||
}
|
||||
|
|
@ -351,7 +378,7 @@ export default {
|
|||
initForm () {
|
||||
this.formRef = ref()
|
||||
this.form = reactive({
|
||||
action: 'START',
|
||||
action: this.actions.length > 0 ? this.actions[0].value : null,
|
||||
schedule: '* * * * *',
|
||||
description: '',
|
||||
timezone: 'UTC',
|
||||
|
|
@ -368,14 +395,15 @@ export default {
|
|||
endDate: [{ required: false, message: `${this.$t('message.error.select')}` }]
|
||||
})
|
||||
},
|
||||
createVMSchedule (schedule) {
|
||||
createSchedule (schedule) {
|
||||
this.resetForm()
|
||||
this.showAddModal()
|
||||
},
|
||||
removeVMSchedule (schedule) {
|
||||
postAPI('deleteVMSchedule', {
|
||||
removeSchedule (schedule) {
|
||||
postAPI('deleteResourceSchedule', {
|
||||
id: schedule.id,
|
||||
virtualmachineid: this.virtualmachine.id
|
||||
resourceid: this.resource.id,
|
||||
resourcetype: this.resourceType
|
||||
}).then(() => {
|
||||
if (this.totalCount - 1 === this.pageSize * (this.page - 1)) {
|
||||
this.page = this.page - 1 > 0 ? this.page - 1 : 1
|
||||
|
|
@ -384,16 +412,16 @@ export default {
|
|||
this.$message.success(message)
|
||||
}).catch(error => {
|
||||
console.error(error)
|
||||
this.$message.error(this.$t('message.error.remove.vm.schedule'))
|
||||
this.$message.error(this.$t('message.error.remove.resource.schedule'))
|
||||
this.$notification.error({
|
||||
message: this.$t('label.error'),
|
||||
description: this.$t('message.error.remove.vm.schedule')
|
||||
description: this.$t('message.error.remove.resource.schedule')
|
||||
})
|
||||
}).finally(() => {
|
||||
this.fetchData()
|
||||
})
|
||||
},
|
||||
updateVMSchedule (schedule) {
|
||||
updateSchedule (schedule) {
|
||||
this.resetForm()
|
||||
this.isEdit = true
|
||||
Object.assign(this.form, schedule)
|
||||
|
|
@ -416,23 +444,24 @@ export default {
|
|||
schedule: values.schedule,
|
||||
timezone: values.timezone,
|
||||
action: values.action,
|
||||
virtualmachineid: this.virtualmachine.id,
|
||||
resourceid: this.resource.id,
|
||||
resourcetype: this.resourceType,
|
||||
enabled: values.enabled,
|
||||
startdate: (values.startDate) ? values.startDate.format(this.pattern) : null,
|
||||
enddate: (values.endDate) ? values.endDate.format(this.pattern) : null
|
||||
}
|
||||
let command = null
|
||||
if (this.form.id === null || this.form.id === undefined) {
|
||||
command = 'createVMSchedule'
|
||||
command = 'createResourceSchedule'
|
||||
} else {
|
||||
params.id = this.form.id
|
||||
command = 'updateVMSchedule'
|
||||
command = 'updateResourceSchedule'
|
||||
}
|
||||
|
||||
postAPI(command, params).then(response => {
|
||||
this.$notification.success({
|
||||
message: this.$t('label.schedule'),
|
||||
description: this.$t('message.success.config.vm.schedule')
|
||||
description: this.$t('message.success.config.resource.schedule')
|
||||
})
|
||||
this.isSubmitted = false
|
||||
this.fetchData()
|
||||
|
|
@ -475,20 +504,25 @@ export default {
|
|||
},
|
||||
fetchSchedules () {
|
||||
this.schedules = []
|
||||
if (!this.virtualmachine.id) {
|
||||
if (!this.resource.id) {
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
page: this.page,
|
||||
pagesize: this.pageSize,
|
||||
virtualmachineid: this.virtualmachine.id,
|
||||
resourceid: this.resource.id,
|
||||
resourcetype: this.resourceType,
|
||||
listall: true
|
||||
}
|
||||
this.tabLoading = true
|
||||
getAPI('listVMSchedule', params).then(json => {
|
||||
getAPI('listResourceSchedule', params).then(json => {
|
||||
this.schedules = []
|
||||
this.totalCount = json?.listvmscheduleresponse?.count || 0
|
||||
this.schedules = json?.listvmscheduleresponse?.vmschedule || []
|
||||
this.totalCount = json?.listresourcescheduleresponse?.count || 0
|
||||
this.schedules = json?.listresourcescheduleresponse?.resourceschedule || []
|
||||
}).catch(error => {
|
||||
console.error(error)
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.tabLoading = false
|
||||
})
|
||||
},
|
||||
Loading…
Reference in New Issue