CLOUDSTACK-6987: Add support for storing metadata for snapshot policy and controlling policy using display flag

(cherry picked from commit 5cf8edd7ec)
This commit is contained in:
Nitin Mehta 2014-06-24 14:06:54 -07:00
parent c344693e48
commit cb5e8c591f
19 changed files with 222 additions and 28 deletions

View File

@ -55,7 +55,8 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit
AutoScaleVmProfile(false, true),
AutoScaleVmGroup(false, true),
LBStickinessPolicy(false, true),
LBHealthCheckPolicy(false, true);
LBHealthCheckPolicy(false, true),
SnapshotPolicy(false, true);
ResourceObjectType(boolean resourceTagsSupport, boolean resourceMetadataSupport) {

View File

@ -16,10 +16,11 @@
// under the License.
package com.cloud.storage.snapshot;
import org.apache.cloudstack.api.Displayable;
import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity;
public interface SnapshotPolicy extends Identity, InternalIdentity {
public interface SnapshotPolicy extends Identity, InternalIdentity, Displayable {
long getVolumeId();

View File

@ -16,6 +16,7 @@
// under the License.
package org.apache.cloudstack.api.command.user.snapshot;
import org.apache.cloudstack.acl.RoleType;
import org.apache.log4j.Logger;
import org.apache.cloudstack.api.APICommand;
@ -64,6 +65,9 @@ public class CreateSnapshotPolicyCmd extends BaseCmd {
@Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "the ID of the disk volume")
private Long volumeId;
@Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the policy to the end user or not", since = "4.4", authorized = {RoleType.Admin})
private Boolean display;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -88,6 +92,14 @@ public class CreateSnapshotPolicyCmd extends BaseCmd {
return volumeId;
}
@Override
public boolean isDisplay() {
if(display == null)
return true;
else
return display;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -19,6 +19,7 @@ package org.apache.cloudstack.api.command.user.snapshot;
import java.util.ArrayList;
import java.util.List;
import org.apache.cloudstack.acl.RoleType;
import org.apache.log4j.Logger;
import org.apache.cloudstack.api.APICommand;
@ -46,6 +47,9 @@ public class ListSnapshotPoliciesCmd extends BaseListCmd {
@Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, required = true, description = "the ID of the disk volume")
private Long volumeId;
@Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin})
private Boolean display;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -54,6 +58,14 @@ public class ListSnapshotPoliciesCmd extends BaseListCmd {
return volumeId;
}
@Override
public boolean isDisplay() {
if (display != null) {
return display;
}
return true;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -18,6 +18,8 @@ package org.apache.cloudstack.api.response;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.EntityReference;
@ -50,6 +52,10 @@ public class SnapshotPolicyResponse extends BaseResponse {
@Param(description = "the time zone of the snapshot policy")
private String timezone;
@SerializedName(ApiConstants.FOR_DISPLAY)
@Param(description = "is this policy for display to the regular user", since = "4.4", authorized = {RoleType.Admin})
private Boolean forDisplay;
public String getId() {
return id;
}
@ -97,4 +103,12 @@ public class SnapshotPolicyResponse extends BaseResponse {
public void setTimezone(String timezone) {
this.timezone = timezone;
}
public Boolean getForDisplay() {
return forDisplay;
}
public void setForDisplay(Boolean forDisplay) {
this.forDisplay = forDisplay;
}
}

View File

@ -254,6 +254,7 @@
<bean id="snapshotDaoImpl" class="com.cloud.storage.dao.SnapshotDaoImpl" />
<bean id="snapshotDetailsDaoImpl" class="com.cloud.storage.dao.SnapshotDetailsDaoImpl" />
<bean id="snapshotPolicyDaoImpl" class="com.cloud.storage.dao.SnapshotPolicyDaoImpl" />
<bean id="snapshotPolicyDetailsDaoImpl" class="org.apache.cloudstack.resourcedetail.dao.SnapshotPolicyDetailsDaoImpl" />
<bean id="snapshotScheduleDaoImpl" class="com.cloud.storage.dao.SnapshotScheduleDaoImpl" />
<bean id="sslCertDao" class="com.cloud.network.dao.SslCertDaoImpl" />
<bean id="staticRouteDaoImpl" class="com.cloud.network.vpc.dao.StaticRouteDaoImpl" />

View File

@ -58,17 +58,21 @@ public class SnapshotPolicyVO implements SnapshotPolicy {
@Column(name = "uuid")
String uuid;
@Column(name = "display", updatable = true, nullable = false)
protected boolean display = true;
public SnapshotPolicyVO() {
this.uuid = UUID.randomUUID().toString();
}
public SnapshotPolicyVO(long volumeId, String schedule, String timezone, IntervalType intvType, int maxSnaps) {
public SnapshotPolicyVO(long volumeId, String schedule, String timezone, IntervalType intvType, int maxSnaps, boolean display) {
this.volumeId = volumeId;
this.schedule = schedule;
this.timezone = timezone;
this.interval = (short)intvType.ordinal();
this.maxSnaps = maxSnaps;
this.active = true;
this.display = display;
this.uuid = UUID.randomUUID().toString();
}
@ -140,4 +144,12 @@ public class SnapshotPolicyVO implements SnapshotPolicy {
public void setUuid(String uuid) {
this.uuid = uuid;
}
public boolean isDisplay() {
return display;
}
public void setDisplay(boolean display) {
this.display = display;
}
}

View File

@ -32,9 +32,9 @@ public interface SnapshotPolicyDao extends GenericDao<SnapshotPolicyVO, Long> {
List<SnapshotPolicyVO> listByVolumeId(long volumeId, Filter filter);
Pair<List<SnapshotPolicyVO>, Integer> listAndCountByVolumeId(long volumeId);
Pair<List<SnapshotPolicyVO>, Integer> listAndCountByVolumeId(long volumeId, boolean display);
Pair<List<SnapshotPolicyVO>, Integer> listAndCountByVolumeId(long volumeId, Filter filter);
Pair<List<SnapshotPolicyVO>, Integer> listAndCountByVolumeId(long volumeId, boolean display, Filter filter);
SnapshotPolicyVO findOneByVolumeInterval(long volumeId, IntervalType intvType);

View File

@ -66,14 +66,15 @@ public class SnapshotPolicyDaoImpl extends GenericDaoBase<SnapshotPolicyVO, Long
}
@Override
public Pair<List<SnapshotPolicyVO>, Integer> listAndCountByVolumeId(long volumeId) {
return listAndCountByVolumeId(volumeId, null);
public Pair<List<SnapshotPolicyVO>, Integer> listAndCountByVolumeId(long volumeId, boolean display) {
return listAndCountByVolumeId(volumeId, display, null);
}
@Override
public Pair<List<SnapshotPolicyVO>, Integer> listAndCountByVolumeId(long volumeId, Filter filter) {
public Pair<List<SnapshotPolicyVO>, Integer> listAndCountByVolumeId(long volumeId, boolean display, Filter filter) {
SearchCriteria<SnapshotPolicyVO> sc = VolumeIdSearch.create();
sc.setParameters("volumeId", volumeId);
sc.setParameters("display", display);
sc.setParameters("active", true);
return searchAndCount(sc, filter);
}
@ -82,6 +83,7 @@ public class SnapshotPolicyDaoImpl extends GenericDaoBase<SnapshotPolicyVO, Long
VolumeIdSearch = createSearchBuilder();
VolumeIdSearch.and("volumeId", VolumeIdSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeIdSearch.and("active", VolumeIdSearch.entity().isActive(), SearchCriteria.Op.EQ);
VolumeIdSearch.and("display", VolumeIdSearch.entity().isDisplay(), SearchCriteria.Op.EQ);
VolumeIdSearch.done();
VolumeIdIntervalSearch = createSearchBuilder();

View File

@ -0,0 +1,65 @@
package org.apache.cloudstack.resourcedetail;
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 = "snapshot_policy_details")
public class SnapshotPolicyDetailVO implements ResourceDetail {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@Column(name = "policy_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 SnapshotPolicyDetailVO() {
}
public SnapshotPolicyDetailVO(long id, String name, String value) {
this.resourceId = id;
this.name = name;
this.value = value;
}
@Override
public long getId() {
return id;
}
@Override
public String getName() {
return name;
}
@Override
public String getValue() {
return value;
}
@Override
public long getResourceId() {
return resourceId;
}
@Override
public boolean isDisplay() {
return display;
}
}

View File

@ -0,0 +1,8 @@
package org.apache.cloudstack.resourcedetail.dao;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.resourcedetail.ResourceDetailsDao;
import org.apache.cloudstack.resourcedetail.SnapshotPolicyDetailVO;
public interface SnapshotPolicyDetailsDao extends GenericDao<SnapshotPolicyDetailVO, Long>, ResourceDetailsDao<SnapshotPolicyDetailVO> {
}

View File

@ -0,0 +1,17 @@
package org.apache.cloudstack.resourcedetail.dao;
import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase;
import org.apache.cloudstack.resourcedetail.SnapshotPolicyDetailVO;
import org.springframework.stereotype.Component;
import javax.ejb.Local;
@Component
@Local(value = {SnapshotPolicyDetailsDao.class})
public class SnapshotPolicyDetailsDaoImpl extends ResourceDetailsDaoBase<SnapshotPolicyDetailVO> implements SnapshotPolicyDetailsDao {
@Override
public void addDetail(long resourceId, String key, String value, boolean display) {
super.addDetail(new SnapshotPolicyDetailVO(resourceId, key, value));
}
}

View File

@ -519,6 +519,7 @@ public class ApiResponseHelper implements ResponseGenerator {
policyResponse.setIntervalType(policy.getInterval());
policyResponse.setMaxSnaps(policy.getMaxSnaps());
policyResponse.setTimezone(policy.getTimezone());
policyResponse.setForDisplay(policy.isDisplay());
policyResponse.setObjectName("snapshotpolicy");
return policyResponse;

View File

@ -36,6 +36,7 @@ import org.apache.cloudstack.resourcedetail.dao.RemoteAccessVpnDetailsDao;
import org.apache.cloudstack.resourcedetail.dao.Site2SiteCustomerGatewayDetailsDao;
import org.apache.cloudstack.resourcedetail.dao.Site2SiteVpnConnectionDetailsDao;
import org.apache.cloudstack.resourcedetail.dao.Site2SiteVpnGatewayDetailsDao;
import org.apache.cloudstack.resourcedetail.dao.SnapshotPolicyDetailsDao;
import org.apache.cloudstack.resourcedetail.dao.UserDetailsDao;
import org.apache.cloudstack.resourcedetail.dao.UserIpAddressDetailsDao;
import org.apache.cloudstack.resourcedetail.dao.VpcDetailsDao;
@ -117,9 +118,11 @@ public class ResourceMetaDataManagerImpl extends ManagerBase implements Resource
@Inject
AutoScaleVmGroupDetailsDao _autoScaleVmGroupDetailsDao;
@Inject
LBStickinessPolicyDetailsDao _stickinessPolicyDao;
LBStickinessPolicyDetailsDao _stickinessPolicyDetailsDao;
@Inject
LBHealthCheckPolicyDetailsDao _healthcheckPolicyDao;
LBHealthCheckPolicyDetailsDao _healthcheckPolicyDetailsDao;
@Inject
SnapshotPolicyDetailsDao _snapshotPolicyDetailsDao;
private static Map<ResourceObjectType, ResourceDetailsDao<? extends ResourceDetail>> s_daoMap = new HashMap<ResourceObjectType, ResourceDetailsDao<? extends ResourceDetail>>();
@ -149,8 +152,9 @@ public class ResourceMetaDataManagerImpl extends ManagerBase implements Resource
s_daoMap.put(ResourceObjectType.User, _userDetailsDao);
s_daoMap.put(ResourceObjectType.AutoScaleVmProfile, _autoScaleVmProfileDetailsDao);
s_daoMap.put(ResourceObjectType.AutoScaleVmGroup, _autoScaleVmGroupDetailsDao);
s_daoMap.put(ResourceObjectType.LBStickinessPolicy, _stickinessPolicyDao);
s_daoMap.put(ResourceObjectType.LBHealthCheckPolicy, _healthcheckPolicyDao);
s_daoMap.put(ResourceObjectType.LBStickinessPolicy, _stickinessPolicyDetailsDao);
s_daoMap.put(ResourceObjectType.LBHealthCheckPolicy, _healthcheckPolicyDetailsDao);
s_daoMap.put(ResourceObjectType.SnapshotPolicy, _snapshotPolicyDetailsDao);
return true;
}

View File

@ -616,6 +616,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
@DB
public SnapshotPolicyVO createPolicy(CreateSnapshotPolicyCmd cmd, Account policyOwner) {
Long volumeId = cmd.getVolumeId();
boolean display = cmd.isDisplay();
VolumeVO volume = _volsDao.findById(cmd.getVolumeId());
if (volume == null) {
throw new InvalidParameterValueException("Failed to create snapshot policy, unable to find a volume with id " + volumeId);
@ -623,7 +624,8 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, volume);
if (volume.getState() != Volume.State.Ready) {
// If display is false we don't actually schedule snapshots.
if (volume.getState() != Volume.State.Ready && display) {
throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() +
". Cannot take snapshot.");
}
@ -670,33 +672,37 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
throw new InvalidParameterValueException("maxSnaps exceeds limit: " + intervalMaxSnaps + " for interval type: " + cmd.getIntervalType());
}
// Verify that max doesn't exceed domain and account snapshot limits
long accountLimit = _resourceLimitMgr.findCorrectResourceLimitForAccount(owner, ResourceType.snapshot);
long domainLimit = _resourceLimitMgr.findCorrectResourceLimitForDomain(_domainMgr.getDomain(owner.getDomainId()), ResourceType.snapshot);
int max = cmd.getMaxSnaps().intValue();
if (!_accountMgr.isRootAdmin(owner.getId())&& ((accountLimit != -1 && max > accountLimit) || (domainLimit != -1 && max > domainLimit))) {
String message = "domain/account";
if (owner.getType() == Account.ACCOUNT_TYPE_PROJECT) {
message = "domain/project";
// Verify that max doesn't exceed domain and account snapshot limits in case display is on
if(display){
long accountLimit = _resourceLimitMgr.findCorrectResourceLimitForAccount(owner, ResourceType.snapshot);
long domainLimit = _resourceLimitMgr.findCorrectResourceLimitForDomain(_domainMgr.getDomain(owner.getDomainId()), ResourceType.snapshot);
int max = cmd.getMaxSnaps().intValue();
if (!_accountMgr.isRootAdmin(owner.getId())&& ((accountLimit != -1 && max > accountLimit) || (domainLimit != -1 && max > domainLimit))) {
String message = "domain/account";
if (owner.getType() == Account.ACCOUNT_TYPE_PROJECT) {
message = "domain/project";
}
throw new InvalidParameterValueException("Max number of snapshots shouldn't exceed the " + message + " level snapshot limit");
}
throw new InvalidParameterValueException("Max number of snapshots shouldn't exceed the " + message + " level snapshot limit");
}
SnapshotPolicyVO policy = _snapshotPolicyDao.findOneByVolumeInterval(volumeId, intvType);
if (policy == null) {
policy = new SnapshotPolicyVO(volumeId, cmd.getSchedule(), timezoneId, intvType, cmd.getMaxSnaps());
policy = new SnapshotPolicyVO(volumeId, cmd.getSchedule(), timezoneId, intvType, cmd.getMaxSnaps(), display);
policy = _snapshotPolicyDao.persist(policy);
_snapSchedMgr.scheduleNextSnapshotJob(policy);
} else {
try {
boolean previousDisplay = policy.isDisplay();
policy = _snapshotPolicyDao.acquireInLockTable(policy.getId());
policy.setSchedule(cmd.getSchedule());
policy.setTimezone(timezoneId);
policy.setInterval((short)intvType.ordinal());
policy.setMaxSnaps(cmd.getMaxSnaps());
policy.setActive(true);
policy.setDisplay(display);
_snapshotPolicyDao.update(policy.getId(), policy);
_snapSchedMgr.scheduleOrCancelNextSnapshotJobOnDisplayChange(policy, previousDisplay);
} finally {
if (policy != null) {
_snapshotPolicyDao.releaseFromLockTable(policy.getId());
@ -716,12 +722,13 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager,
@Override
public Pair<List<? extends SnapshotPolicy>, Integer> listPoliciesforVolume(ListSnapshotPoliciesCmd cmd) {
Long volumeId = cmd.getVolumeId();
boolean display = cmd.isDisplay();
VolumeVO volume = _volsDao.findById(volumeId);
if (volume == null) {
throw new InvalidParameterValueException("Unable to find a volume with id " + volumeId);
}
_accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, volume);
Pair<List<SnapshotPolicyVO>, Integer> result = _snapshotPolicyDao.listAndCountByVolumeId(volumeId);
Pair<List<SnapshotPolicyVO>, Integer> result = _snapshotPolicyDao.listAndCountByVolumeId(volumeId, display);
return new Pair<List<? extends SnapshotPolicy>, Integer>(result.first(), result.second());
}

View File

@ -40,4 +40,6 @@ public interface SnapshotScheduler extends Manager, Scheduler {
* @return
*/
boolean removeSchedule(Long volumeId, Long policyId);
void scheduleOrCancelNextSnapshotJobOnDisplayChange(SnapshotPolicyVO policy, boolean previousDisplay);
}

View File

@ -304,6 +304,12 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
if (policy == null) {
return null;
}
// If display attribute is false then remove schedules if any.
if(!policy.isDisplay()){
removeSchedule(policy.getVolumeId(), policy.getId());
}
final long policyId = policy.getId();
if (policyId == Snapshot.MANUAL_POLICY_ID) {
return null;
@ -330,6 +336,20 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu
return nextSnapshotTimestamp;
}
@Override
public void scheduleOrCancelNextSnapshotJobOnDisplayChange(final SnapshotPolicyVO policy, boolean previousDisplay) {
// Take action only if display changed
if(policy.isDisplay() != previousDisplay ){
if(policy.isDisplay()){
scheduleNextSnapshotJob(policy);
}else{
removeSchedule(policy.getVolumeId(), policy.getId());
}
}
}
@Override
@DB
public boolean removeSchedule(final Long volumeId, final Long policyId) {

View File

@ -25,6 +25,7 @@ import javax.ejb.Local;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.storage.SnapshotPolicyVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.api.Identity;
@ -124,6 +125,7 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso
s_typeMap.put(ResourceObjectType.AutoScaleVmGroup, AutoScaleVmGroupVO.class);
s_typeMap.put(ResourceObjectType.LBStickinessPolicy, LBStickinessPolicyVO.class);
s_typeMap.put(ResourceObjectType.LBHealthCheckPolicy, LBHealthCheckPolicyVO.class);
s_typeMap.put(ResourceObjectType.SnapshotPolicy, SnapshotPolicyVO.class);
}
@ -166,9 +168,9 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso
entity = _entityMgr.findById(clazz, resourceId);
if (entity != null) {
return ((InternalIdentity)entity).getId();
}
throw new InvalidParameterValueException("Unable to find resource by id " + resourceId + " and type " + resourceType);
}
throw new InvalidParameterValueException("Unable to find resource by id " + resourceId + " and type " + resourceType);
}
private Pair<Long, Long> getAccountDomain(long resourceId, ResourceObjectType resourceType) {
Class<?> clazz = s_typeMap.get(resourceType);

View File

@ -1712,3 +1712,16 @@ CREATE TABLE `cloud`.`load_balancer_healthcheck_policy_details` (
PRIMARY KEY (`id`),
CONSTRAINT `fk_lb_healthcheck_policy_details__lb_healthcheck_policy_id` FOREIGN KEY `fk_lb_healthcheck_policy_details__lb_healthcheck_policy_id`(`lb_policy_id`) REFERENCES `load_balancer_healthcheck_policies`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `cloud`.`snapshot_policy` ADD COLUMN `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the policy can be displayed to the end user';
CREATE TABLE `cloud`.`snapshot_policy_details` (
`id` bigint unsigned NOT NULL auto_increment,
`policy_id` bigint unsigned NOT NULL COMMENT 'snapshot policy id',
`name` varchar(255) NOT NULL,
`value` varchar(1024) NOT NULL,
`display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user',
PRIMARY KEY (`id`),
CONSTRAINT `fk_snapshot_policy_details__snapshot_policy_id` FOREIGN KEY `fk_snapshot_policy_details__snapshot_policy_id`(`policy_id`) REFERENCES `snapshot_policy`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;