diff --git a/server/src/com/cloud/api/commands/CreateSnapshotPolicyCmd.java b/server/src/com/cloud/api/commands/CreateSnapshotPolicyCmd.java index 183e954266e..4633bb70d4a 100644 --- a/server/src/com/cloud/api/commands/CreateSnapshotPolicyCmd.java +++ b/server/src/com/cloud/api/commands/CreateSnapshotPolicyCmd.java @@ -18,40 +18,21 @@ package com.cloud.api.commands; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - import org.apache.log4j.Logger; import com.cloud.api.BaseCmd; +import com.cloud.api.BaseCmd.Manager; +import com.cloud.api.Implementation; import com.cloud.api.Parameter; -import com.cloud.api.ServerApiException; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.server.ManagementServer; +import com.cloud.api.response.SnapshotPolicyResponse; +import com.cloud.serializer.SerializerHelper; import com.cloud.storage.SnapshotPolicyVO; -import com.cloud.storage.StoragePoolVO; -import com.cloud.storage.VolumeVO; -import com.cloud.utils.Pair; - + +@Implementation(method="createPolicy", manager=Manager.SnapshotManager) public class CreateSnapshotPolicyCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(CreateSnapshotPolicyCmd.class.getName()); private static final String s_name = "createsnapshotpolicyresponse"; - private static final List> s_properties = new ArrayList>(); - - static { - s_properties.add(new Pair(BaseCmd.Properties.ACCOUNT_OBJ, Boolean.FALSE)); - s_properties.add(new Pair(BaseCmd.Properties.USER_ID, Boolean.FALSE)); - - s_properties.add(new Pair(BaseCmd.Properties.ACCOUNT, Boolean.FALSE)); - s_properties.add(new Pair(BaseCmd.Properties.DOMAIN_ID, Boolean.FALSE)); - s_properties.add(new Pair(BaseCmd.Properties.INTERVAL_TYPE, Boolean.TRUE)); - s_properties.add(new Pair(BaseCmd.Properties.MAX_SNAPS, Boolean.TRUE)); - s_properties.add(new Pair(BaseCmd.Properties.SCHEDULE, Boolean.TRUE)); - s_properties.add(new Pair(BaseCmd.Properties.TIMEZONE, Boolean.TRUE)); - s_properties.add(new Pair(BaseCmd.Properties.VOLUME_ID, Boolean.TRUE)); - } ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -116,74 +97,22 @@ public class CreateSnapshotPolicyCmd extends BaseCmd { /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// + @Override public String getName() { return s_name; } - public List> getProperties() { - return s_properties; - } - - @Override - public List> execute(Map params) { - Long userId = (Long)params.get(BaseCmd.Properties.USER_ID.getName()); - long volumeId = (Long)params.get(BaseCmd.Properties.VOLUME_ID.getName()); - String schedule = (String)params.get(BaseCmd.Properties.SCHEDULE.getName()); - String timezone = (String)params.get(BaseCmd.Properties.TIMEZONE.getName()); - String intervalType = (String)params.get(BaseCmd.Properties.INTERVAL_TYPE.getName()); - //ToDo: make maxSnaps optional. Use system wide max when not specified - int maxSnaps = (Integer)params.get(BaseCmd.Properties.MAX_SNAPS.getName()); - - ManagementServer managementServer = getManagementServer(); - // Verify that a volume exists with the specified volume ID - VolumeVO volume = managementServer.findVolumeById(volumeId); - if (volume == null) { - throw new ServerApiException (BaseCmd.PARAM_ERROR, "Unable to find a volume with id " + volumeId); - } - - // If an account was passed in, make sure that it matches the account of the volume - checkAccountPermissions(params, volume.getAccountId(), volume.getDomainId(), "volume", volumeId); - - StoragePoolVO storagePoolVO = managementServer.findPoolById(volume.getPoolId()); - if (storagePoolVO == null) { - throw new ServerApiException(BaseCmd.PARAM_ERROR, "volumeId: " + volumeId + " does not have a valid storage pool. Is it destroyed?"); - } - if (storagePoolVO.isLocal()) { - throw new ServerApiException(BaseCmd.PARAM_ERROR, "Cannot create a snapshot from a volume residing on a local storage pool, poolId: " + volume.getPoolId()); - } - Long instanceId = volume.getInstanceId(); - if (instanceId != null) { - // It is not detached, but attached to a VM - if (managementServer.findUserVMInstanceById(instanceId) == null) { - // It is not a UserVM but a SystemVM or DomR - throw new ServerApiException(BaseCmd.PARAM_ERROR, "Snapshots of volumes attached to System or router VM are not allowed"); - } - } - - Long accountId = volume.getAccountId(); - // If command is executed via 8096 port, set userId to the id of System account (1) - if (userId == null) { - userId = Long.valueOf(1); - } - - SnapshotPolicyVO snapshotPolicy = null; - try { - snapshotPolicy = managementServer.createSnapshotPolicy(accountId, userId, volumeId, schedule, intervalType, maxSnaps, timezone); - } catch (InvalidParameterValueException ex) { - throw new ServerApiException (BaseCmd.VM_INVALID_PARAM_ERROR, ex.getMessage()); - } - - if (snapshotPolicy == null) { - throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create Snapshot Policy"); - } - - List> returnValues = new ArrayList>(); - returnValues.add(new Pair(BaseCmd.Properties.ID.getName(), snapshotPolicy.getId().toString())); - returnValues.add(new Pair(BaseCmd.Properties.VOLUME_ID.getName(), snapshotPolicy.getVolumeId())); - returnValues.add(new Pair(BaseCmd.Properties.SCHEDULE.getName(), snapshotPolicy.getSchedule())); - returnValues.add(new Pair(BaseCmd.Properties.INTERVAL_TYPE.getName(), snapshotPolicy.getInterval())); - returnValues.add(new Pair(BaseCmd.Properties.MAX_SNAPS.getName(), snapshotPolicy.getMaxSnaps())); - - return returnValues; + @Override + public String getResponse() { + SnapshotPolicyVO snapshotPolicy = (SnapshotPolicyVO)getResponseObject(); + + SnapshotPolicyResponse response = new SnapshotPolicyResponse(); + response.setId(snapshotPolicy.getId()); + response.setIntervalType(snapshotPolicy.getInterval()); + response.setMaxSnaps(snapshotPolicy.getMaxSnaps()); + response.setSchedule(snapshotPolicy.getSchedule()); + response.setVolumeId(snapshotPolicy.getVolumeId()); + + return SerializerHelper.toSerializedString(response); } } diff --git a/server/src/com/cloud/api/response/SnapshotPolicyResponse.java b/server/src/com/cloud/api/response/SnapshotPolicyResponse.java new file mode 100644 index 00000000000..106568015aa --- /dev/null +++ b/server/src/com/cloud/api/response/SnapshotPolicyResponse.java @@ -0,0 +1,61 @@ +package com.cloud.api.response; + +import com.cloud.api.ResponseObject; +import com.cloud.serializer.Param; + +public class SnapshotPolicyResponse implements ResponseObject { + @Param(name="id") + private Long id; + + @Param(name="volumeid") + private Long volumeId; + + @Param(name="schedule") + private String schedule; + + @Param(name="intervaltype") + private short intervalType; + + @Param(name="maxsnaps") + private int maxSnaps; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getVolumeId() { + return volumeId; + } + + public void setVolumeId(Long volumeId) { + this.volumeId = volumeId; + } + + public String getSchedule() { + return schedule; + } + + public void setSchedule(String schedule) { + this.schedule = schedule; + } + + public short getIntervalType() { + return intervalType; + } + + public void setIntervalType(short intervalType) { + this.intervalType = intervalType; + } + + public int getMaxSnaps() { + return maxSnaps; + } + + public void setMaxSnaps(int maxSnaps) { + this.maxSnaps = maxSnaps; + } +} diff --git a/server/src/com/cloud/server/ManagementServer.java b/server/src/com/cloud/server/ManagementServer.java index b5a02e1cae3..9cef7000e9d 100644 --- a/server/src/com/cloud/server/ManagementServer.java +++ b/server/src/com/cloud/server/ManagementServer.java @@ -1877,19 +1877,6 @@ public interface ManagementServer { StoragePoolVO addPool(Long zoneId, Long podId, Long clusterId, String poolName, String storageUri, String tags, Map details) throws ResourceInUseException, URISyntaxException, IllegalArgumentException, UnknownHostException, ResourceAllocationException; List searchForStoragePools(Criteria c); - /** - * Creates a policy with specified schedule to create snapshot for a volume . maxSnaps specifies the number of most recent snapshots that are to be retained. - * @param volumeId - * @param schedule MM[:HH][:DD] format. DD is day of week for weekly[1-7] and day of month for monthly - * @param interval hourly/daily/weekly/monthly - * @param maxSnaps If the number of snapshots go beyond maxSnaps the oldest snapshot is deleted - * @param timezone The timezone in which the above time format is specified - * @return - * @throws InvalidParameterValueException - */ - SnapshotPolicyVO createSnapshotPolicy(long userId, long accountId, long volumeId, String schedule, String intervalType, - int maxSnaps, String timezone) throws InvalidParameterValueException; - /** * List all snapshot policies which are created for the specified volume * @param volumeId diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 0f5688d26c0..a3bc66483a9 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -7734,33 +7734,6 @@ public class ManagementServerImpl implements ManagementServer { return _jobDao.search(sc, searchFilter); } - @Override - public SnapshotPolicyVO createSnapshotPolicy(long userId, long accountId, long volumeId, String schedule, String intervalType, int maxSnaps, String timeZoneStr) throws InvalidParameterValueException { - IntervalType type = DateUtil.IntervalType.getIntervalType(intervalType); - if(type == null){ - throw new InvalidParameterValueException("Unsupported interval type " + intervalType); - } - - TimeZone timeZone = TimeZone.getTimeZone(timeZoneStr); - String timezoneId = timeZone.getID(); - if (!timezoneId.equals(timeZoneStr)) { - s_logger.warn("Using timezone: " + timezoneId + " for running this snapshot policy as an equivalent of " + timeZoneStr); - } - - try { - DateUtil.getNextRunTime(type, schedule, timezoneId, null); - } catch (Exception e){ - throw new InvalidParameterValueException("Invalid schedule: "+ schedule +" for interval type: " + intervalType); - } - - int intervalMaxSnaps = type.getMax(); - if(maxSnaps > intervalMaxSnaps){ - throw new InvalidParameterValueException("maxSnaps exceeds limit: "+ intervalMaxSnaps +" for interval type: " + intervalType); - } - - return _snapMgr.createPolicy(userId, accountId, volumeId, schedule, (short)type.ordinal() , maxSnaps, timezoneId); - } - @Override public SnapshotPolicyVO findSnapshotPolicyById(Long policyId) { return _snapshotPolicyDao.findById(policyId); diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManager.java b/server/src/com/cloud/storage/snapshot/SnapshotManager.java index e445b437d3c..b61766b4872 100644 --- a/server/src/com/cloud/storage/snapshot/SnapshotManager.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManager.java @@ -19,13 +19,13 @@ package com.cloud.storage.snapshot; import java.util.List; +import com.cloud.api.commands.CreateSnapshotPolicyCmd; import com.cloud.exception.InternalErrorException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.storage.SnapshotPolicyVO; import com.cloud.storage.SnapshotScheduleVO; import com.cloud.storage.SnapshotVO; -import com.cloud.storage.VolumeVO; import com.cloud.utils.component.Manager; import com.cloud.utils.db.Filter; @@ -94,16 +94,14 @@ public interface SnapshotManager extends Manager { * This will be used for manual snapshots too. */ boolean deleteSnapshot(long userId, long snapshotId, long policyId); - /** * Creates a policy with specified schedule. maxSnaps specifies the number of most recent snapshots that are to be retained. * If the number of snapshots go beyond maxSnaps the oldest snapshot is deleted - * @param volumeId - * @param userId - * @param timeZone The timezone in which the 'schedule' string is specified + * @param cmd the command that + * @return the newly created snapshot policy if success, null otherwise */ - SnapshotPolicyVO createPolicy(long userId, long accountId, long volumeId, String schedule, short interval, int maxSnaps, String timezone); + SnapshotPolicyVO createPolicy(CreateSnapshotPolicyCmd cmd) throws InvalidParameterValueException; /** * Deletes snapshot scheduling policy. Delete will fail if this policy is assigned to one or more volumes diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index c13da3e27bc..92d6ca0d5a2 100644 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.TimeZone; import javax.ejb.Local; import javax.naming.ConfigurationException; @@ -40,6 +41,7 @@ import com.cloud.agent.api.ValidateSnapshotCommand; import com.cloud.agent.manager.AgentManager; import com.cloud.api.BaseCmd; import com.cloud.api.commands.CreateSnapshotCmd; +import com.cloud.api.commands.CreateSnapshotPolicyCmd; import com.cloud.api.commands.CreateVolumeCmd; import com.cloud.async.AsyncJobExecutor; import com.cloud.async.AsyncJobManager; @@ -68,6 +70,7 @@ import com.cloud.storage.SnapshotScheduleVO; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageManager; +import com.cloud.storage.StoragePoolVO; import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume.VolumeType; @@ -84,9 +87,12 @@ import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; import com.cloud.utils.DateUtil; +import com.cloud.utils.DateUtil.IntervalType; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Inject; @@ -134,7 +140,6 @@ public class SnapshotManagerImpl implements SnapshotManager { protected SearchBuilder PolicySnapshotSearch; protected SearchBuilder PoliciesForSnapSearch; - private String _hypervisorType; private final boolean _shouldBeSnapshotCapable = true; // all methods here should be snapshot capable. @Override @DB @@ -1071,9 +1076,66 @@ public class SnapshotManagerImpl implements SnapshotManager { @Override @DB - public SnapshotPolicyVO createPolicy(long userId, long accountId, long volumeId, String schedule, short interval, int maxSnaps, String timezone) { + public SnapshotPolicyVO createPolicy(CreateSnapshotPolicyCmd cmd) throws InvalidParameterValueException { + Long volumeId = cmd.getVolumeId(); + 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); + } + + // TODO: implement + // If an account was passed in, make sure that it matches the account of the volume +// checkAccountPermissions(params, volume.getAccountId(), volume.getDomainId(), "volume", volumeId); + + StoragePoolVO storagePoolVO = _storagePoolDao.findById(volume.getPoolId()); + if (storagePoolVO == null) { + throw new InvalidParameterValueException("Failed to create snapshot policy, volumeId: " + volumeId + " does not have a valid storage pool. Is it destroyed?"); + } + if (storagePoolVO.isLocal()) { + throw new InvalidParameterValueException("Failed to create snapshot policy, cannot create a snapshot from a volume residing on a local storage pool, poolId: " + volume.getPoolId()); + } + + Long instanceId = volume.getInstanceId(); + if (instanceId != null) { + // It is not detached, but attached to a VM + if (_vmDao.findById(instanceId) == null) { + // It is not a UserVM but a SystemVM or DomR + throw new InvalidParameterValueException("Failed to create snapshot policy, snapshots of volumes attached to System or router VM are not allowed"); + } + } + + Long accountId = volume.getAccountId(); + Long userId = UserContext.current().getUserId(); + + // If command is executed via 8096 port, set userId to the id of System account (1) + if (userId == null) { + userId = User.UID_SYSTEM; + } + + IntervalType type = DateUtil.IntervalType.getIntervalType(cmd.getIntervalType()); + if (type == null) { + throw new InvalidParameterValueException("Unsupported interval type " + cmd.getIntervalType()); + } + + TimeZone timeZone = TimeZone.getTimeZone(cmd.getTimezone()); + String timezoneId = timeZone.getID(); + if (!timezoneId.equals(cmd.getTimezone())) { + s_logger.warn("Using timezone: " + timezoneId + " for running this snapshot policy as an equivalent of " + cmd.getTimezone()); + } + + try { + DateUtil.getNextRunTime(type, cmd.getSchedule(), timezoneId, null); + } catch (Exception e){ + throw new InvalidParameterValueException("Invalid schedule: "+ cmd.getSchedule() +" for interval type: " + cmd.getIntervalType()); + } + + int intervalMaxSnaps = type.getMax(); + if (cmd.getMaxSnaps() > intervalMaxSnaps) { + throw new InvalidParameterValueException("maxSnaps exceeds limit: " + intervalMaxSnaps + " for interval type: " + cmd.getIntervalType()); + } + Long policyId = null; - SnapshotPolicyVO policy = getPolicyForVolumeByInterval(volumeId, (interval)); + SnapshotPolicyVO policy = getPolicyForVolumeByInterval(volumeId, (short)type.ordinal()); Transaction txn = Transaction.currentTxn(); txn.start(); @@ -1082,30 +1144,34 @@ public class SnapshotManagerImpl implements SnapshotManager { event.setAccountId(accountId); event.setUserId(userId); - if( policy != null){ - s_logger.debug("Policy for specified interval already exists. Updating policy to new schedule"); + if (policy != null) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Policy for specified interval already exists. Updating policy to new schedule"); + } policyId = policy.getId(); - + // By default, assume failure. event.setType(EventTypes.EVENT_SNAPSHOT_POLICY_UPDATE); - event.setDescription("Failed to update schedule for Snapshot policy with id: "+policyId); + event.setDescription("Failed to update schedule for Snapshot policy with id: " + policyId); event.setLevel(EventVO.LEVEL_ERROR); - + // Check if there are any recurring snapshots being currently executed. Race condition. SnapshotScheduleVO snapshotSchedule = _snapshotScheduleDao.getCurrentSchedule(volumeId, policyId, true); if (snapshotSchedule != null) { Date scheduledTimestamp = snapshotSchedule.getScheduledTimestamp(); String dateDisplay = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp); - s_logger.debug("Cannot update the policy now. Wait until the current snapshot scheduled at " + dateDisplay + " finishes"); - + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cannot update the policy now. Wait until the current snapshot scheduled at " + dateDisplay + " finishes"); + } + policyId = null; policy = null; } else { _snapSchedMgr.removeSchedule(volumeId, policyId); - policy.setSchedule(schedule); - policy.setTimezone(timezone); - policy.setMaxSnaps(maxSnaps); + policy.setSchedule(cmd.getSchedule()); + policy.setTimezone(cmd.getTimezone()); + policy.setMaxSnaps(cmd.getMaxSnaps()); policy.setActive(true); if(_snapshotPolicyDao.update(policy.getId(), policy)){ @@ -1114,7 +1180,7 @@ public class SnapshotManagerImpl implements SnapshotManager { } } } else { - policy = new SnapshotPolicyVO(volumeId, schedule, timezone, interval, maxSnaps); + policy = new SnapshotPolicyVO(volumeId, cmd.getSchedule(), cmd.getTimezone(), (short)type.ordinal(), cmd.getMaxSnaps()); policy = _snapshotPolicyDao.persist(policy); policyId = policy.getId(); event.setType(EventTypes.EVENT_SNAPSHOT_POLICY_CREATE); @@ -1126,10 +1192,12 @@ public class SnapshotManagerImpl implements SnapshotManager { _snapSchedMgr.scheduleNextSnapshotJob(policy); } else { - s_logger.debug("Failed to update schedule for Snapshot policy with id: " + policyId); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Failed to update schedule for Snapshot policy with id: " + policyId); + } } txn.commit(); - + return policy; } @@ -1295,8 +1363,6 @@ public class SnapshotManagerImpl implements SnapshotManager { throw new ConfigurationException("Unable to get the configuration dao."); } - _hypervisorType = configDao.getValue("hypervisor.type"); - DateUtil.IntervalType.HOURLY.setMax(NumbersUtil.parseInt(configDao.getValue("snapshot.max.hourly"), HOURLYMAX)); DateUtil.IntervalType.DAILY.setMax(NumbersUtil.parseInt(configDao.getValue("snapshot.max.daily"), DAILYMAX)); DateUtil.IntervalType.WEEKLY.setMax(NumbersUtil.parseInt(configDao.getValue("snapshot.max.weekly"), WEEKLYMAX));