From f539f40e3505e80a60b75b3e98fbab269a0fbde3 Mon Sep 17 00:00:00 2001 From: Sanjay Tripathi Date: Wed, 27 Feb 2013 11:39:58 +0530 Subject: [PATCH] CLOUDSTACK-874: Ability to delete Events and Alerts This feature will provide the functionality to delete or archive the Alerts/Events. Delete or archive alerts APIs are available only for root-admin, delete or archive events are available for regular users. --- api/src/com/cloud/alert/Alert.java | 1 + api/src/com/cloud/event/Event.java | 1 + .../com/cloud/server/ManagementService.java | 38 +++- .../apache/cloudstack/api/ApiConstants.java | 1 + .../admin/resource/ArchiveAlertsCmd.java | 100 +++++++++++ .../admin/resource/DeleteAlertsCmd.java | 99 +++++++++++ .../command/user/event/ArchiveEventsCmd.java | 105 +++++++++++ .../command/user/event/DeleteEventsCmd.java | 105 +++++++++++ client/tomcatconf/commands.properties.in | 4 + core/src/com/cloud/alert/AlertVO.java | 38 ++-- core/src/com/cloud/event/EventVO.java | 163 +++++++++--------- core/src/com/cloud/event/dao/EventDao.java | 5 + .../src/com/cloud/event/dao/EventDaoImpl.java | 71 ++++++-- server/src/com/cloud/alert/dao/AlertDao.java | 7 + .../src/com/cloud/alert/dao/AlertDaoImpl.java | 85 +++++++++ server/src/com/cloud/api/ApiDispatcher.java | 6 +- .../com/cloud/api/query/QueryManagerImpl.java | 3 + .../com/cloud/api/query/vo/EventJoinVO.java | 9 + .../src/com/cloud/configuration/Config.java | 5 +- .../cloud/server/ManagementServerImpl.java | 109 +++++++++++- .../cloud/alert/AlertControlsUnitTest.java | 67 +++++++ .../cloud/event/EventControlsUnitTest.java | 68 ++++++++ setup/db/db/schema-410to420.sql | 45 +++++ 23 files changed, 1017 insertions(+), 118 deletions(-) create mode 100644 api/src/org/apache/cloudstack/api/command/admin/resource/ArchiveAlertsCmd.java create mode 100644 api/src/org/apache/cloudstack/api/command/admin/resource/DeleteAlertsCmd.java create mode 100644 api/src/org/apache/cloudstack/api/command/user/event/ArchiveEventsCmd.java create mode 100644 api/src/org/apache/cloudstack/api/command/user/event/DeleteEventsCmd.java create mode 100644 server/test/com/cloud/alert/AlertControlsUnitTest.java create mode 100644 server/test/com/cloud/event/EventControlsUnitTest.java diff --git a/api/src/com/cloud/alert/Alert.java b/api/src/com/cloud/alert/Alert.java index 050f97f2ef3..31768cf193d 100644 --- a/api/src/com/cloud/alert/Alert.java +++ b/api/src/com/cloud/alert/Alert.java @@ -30,4 +30,5 @@ public interface Alert extends Identity, InternalIdentity { Date getCreatedDate(); Date getLastSent(); Date getResolved(); + boolean getArchived(); } diff --git a/api/src/com/cloud/event/Event.java b/api/src/com/cloud/event/Event.java index 1a61636828a..b8def4c6281 100644 --- a/api/src/com/cloud/event/Event.java +++ b/api/src/com/cloud/event/Event.java @@ -40,4 +40,5 @@ public interface Event extends ControlledEntity, Identity, InternalIdentity { String getLevel(); long getStartId(); String getParameters(); + boolean getArchived(); } diff --git a/api/src/com/cloud/server/ManagementService.java b/api/src/com/cloud/server/ManagementService.java index 1736da3778c..1e6ca8d0b67 100755 --- a/api/src/com/cloud/server/ManagementService.java +++ b/api/src/com/cloud/server/ManagementService.java @@ -29,6 +29,8 @@ import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd; import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd; import org.apache.cloudstack.api.command.admin.pod.ListPodsByCmd; +import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd; +import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd; import org.apache.cloudstack.api.command.admin.resource.ListAlertsCmd; import org.apache.cloudstack.api.command.admin.resource.ListCapacityCmd; import org.apache.cloudstack.api.command.admin.resource.UploadCustomCertificateCmd; @@ -40,12 +42,12 @@ import org.apache.cloudstack.api.command.admin.systemvm.UpgradeSystemVMCmd; import org.apache.cloudstack.api.command.admin.vlan.ListVlanIpRangesCmd; import org.apache.cloudstack.api.command.user.address.ListPublicIpAddressesCmd; import org.apache.cloudstack.api.command.user.config.ListCapabilitiesCmd; +import org.apache.cloudstack.api.command.user.event.ArchiveEventsCmd; +import org.apache.cloudstack.api.command.user.event.DeleteEventsCmd; import org.apache.cloudstack.api.command.user.guest.ListGuestOsCategoriesCmd; import org.apache.cloudstack.api.command.user.guest.ListGuestOsCmd; import org.apache.cloudstack.api.command.user.iso.ListIsosCmd; import org.apache.cloudstack.api.command.user.iso.UpdateIsoCmd; -import org.apache.cloudstack.api.command.user.offering.ListDiskOfferingsCmd; -import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd; import org.apache.cloudstack.api.command.user.ssh.CreateSSHKeyPairCmd; import org.apache.cloudstack.api.command.user.ssh.DeleteSSHKeyPairCmd; import org.apache.cloudstack.api.command.user.ssh.ListSSHKeyPairsCmd; @@ -55,12 +57,10 @@ import org.apache.cloudstack.api.command.user.template.UpdateTemplateCmd; import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd; import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd; import org.apache.cloudstack.api.command.user.volume.ExtractVolumeCmd; -import org.apache.cloudstack.api.command.user.zone.ListZonesByCmd; import com.cloud.alert.Alert; import com.cloud.capacity.Capacity; import com.cloud.configuration.Configuration; -import com.cloud.dc.DataCenter; import com.cloud.dc.Pod; import com.cloud.dc.Vlan; import com.cloud.domain.Domain; @@ -72,8 +72,6 @@ import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.HypervisorCapabilities; import com.cloud.network.IpAddress; -import com.cloud.offering.DiskOffering; -import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOsCategory; @@ -194,6 +192,34 @@ public interface ManagementService { */ Pair, Integer> searchForAlerts(ListAlertsCmd cmd); + /** + * Archive alerts + * @param cmd + * @return True on success. False otherwise. + */ + boolean archiveAlerts(ArchiveAlertsCmd cmd); + + /** + * Delete alerts + * @param cmd + * @return True on success. False otherwise. + */ + boolean deleteAlerts(DeleteAlertsCmd cmd); + + /** + * Archive events + * @param cmd + * @return True on success. False otherwise. + */ + boolean archiveEvents(ArchiveEventsCmd cmd); + + /** + * Delete events + * @param cmd + * @return True on success. False otherwise. + */ + boolean deleteEvents(DeleteEventsCmd cmd); + /** * list all the capacity rows in capacity operations table * diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 1b544fd1641..b40b26ce57c 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -459,6 +459,7 @@ public class ApiConstants { public static final String UCS_BLADE_DN = "bladedn"; public static final String UCS_BLADE_ID = "bladeid"; public static final String VM_GUEST_IP = "vmguestip"; + public static final String OLDER_THAN = "olderthan"; public enum HostDetails { all, capacity, events, stats, min; diff --git a/api/src/org/apache/cloudstack/api/command/admin/resource/ArchiveAlertsCmd.java b/api/src/org/apache/cloudstack/api/command/admin/resource/ArchiveAlertsCmd.java new file mode 100644 index 00000000000..2a1a47a13ed --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/resource/ArchiveAlertsCmd.java @@ -0,0 +1,100 @@ +// 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.admin.resource; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AlertResponse; +import org.apache.cloudstack.api.response.ConditionResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; + +@APICommand(name = "archiveAlerts", description = "Archive one or more alerts.", responseObject = SuccessResponse.class) +public class ArchiveAlertsCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(ArchiveAlertsCmd.class.getName()); + + private static final String s_name = "archivealertsresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AlertResponse.class, + description = "the IDs of the alerts") + private List ids; + + @Parameter(name=ApiConstants.OLDER_THAN, type=CommandType.DATE, description="archive alerts older than this date (use format \"yyyy-MM-dd\")") + private Date olderThan; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "archive by alert type") + private String type; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + public Date getOlderThan() { + return olderThan; + } + + public String getType() { + return type; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + if(ids == null && type == null && olderThan == null) { + throw new InvalidParameterValueException("either ids, type or olderthan must be specified"); + } + boolean result = _mgr.archiveAlerts(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to archive Alerts, one or more parameters has invalid values"); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/resource/DeleteAlertsCmd.java b/api/src/org/apache/cloudstack/api/command/admin/resource/DeleteAlertsCmd.java new file mode 100644 index 00000000000..f03793c4f6d --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/resource/DeleteAlertsCmd.java @@ -0,0 +1,99 @@ +// 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.admin.resource; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AlertResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; + +@APICommand(name = "deleteAlerts", description = "Delete one or more alerts.", responseObject = SuccessResponse.class) +public class DeleteAlertsCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(DeleteAlertsCmd.class.getName()); + + private static final String s_name = "deletealertsresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AlertResponse.class, + description = "the IDs of the alerts") + private List ids; + + @Parameter(name=ApiConstants.OLDER_THAN, type=CommandType.DATE, description="delete alerts older than (including) this date (use format \"yyyy-MM-dd\")") + private Date olderThan; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "delete by alert type") + private String type; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + public Date getOlderThan() { + return olderThan; + } + + public String getType() { + return type; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + if(ids == null && type == null && olderThan == null) { + throw new InvalidParameterValueException("either ids, type or olderthan must be specified"); + } + boolean result = _mgr.deleteAlerts(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to delete Alerts, one or more parameters has invalid values"); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/event/ArchiveEventsCmd.java b/api/src/org/apache/cloudstack/api/command/user/event/ArchiveEventsCmd.java new file mode 100644 index 00000000000..481607c9f0b --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/event/ArchiveEventsCmd.java @@ -0,0 +1,105 @@ +//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.event; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AlertResponse; +import org.apache.cloudstack.api.response.EventResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.user.UserContext; + +@APICommand(name = "archiveEvents", description = "Archive one or more events.", responseObject = SuccessResponse.class) +public class ArchiveEventsCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(ArchiveEventsCmd.class.getName()); + + private static final String s_name = "archiveeventsresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = EventResponse.class, + description = "the IDs of the events") + private List ids; + + @Parameter(name=ApiConstants.OLDER_THAN, type=CommandType.DATE, description="archive events older than (including) this date (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\")") + private Date olderThan; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "archive by event type") + private String type; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + public Date getOlderThan() { + return olderThan; + } + + public String getType() { + return type; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = UserContext.current().getCaller(); + if (account != null) { + return account.getId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + if(ids == null && type == null && olderThan == null) { + throw new InvalidParameterValueException("either ids, type or olderthan must be specified"); + } + boolean result = _mgr.archiveEvents(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to archive Events, one or more parameters has invalid values"); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/event/DeleteEventsCmd.java b/api/src/org/apache/cloudstack/api/command/user/event/DeleteEventsCmd.java new file mode 100644 index 00000000000..55ca92a5dfe --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/event/DeleteEventsCmd.java @@ -0,0 +1,105 @@ +//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.event; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AlertResponse; +import org.apache.cloudstack.api.response.EventResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.user.UserContext; + +@APICommand(name = "deleteEvents", description = "Delete one or more events.", responseObject = SuccessResponse.class) +public class DeleteEventsCmd extends BaseCmd { + + public static final Logger s_logger = Logger.getLogger(DeleteEventsCmd.class.getName()); + + private static final String s_name = "deleteeventsresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = EventResponse.class, + description = "the IDs of the events") + private List ids; + + @Parameter(name=ApiConstants.OLDER_THAN, type=CommandType.DATE, description="delete events older than (including) this date (use format \"yyyy-MM-dd\" or the new format \"yyyy-MM-dd HH:mm:ss\")") + private Date olderThan; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, description = "delete by event type") + private String type; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + public Date getOlderThan() { + return olderThan; + } + + public String getType() { + return type; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = UserContext.current().getCaller(); + if (account != null) { + return account.getId(); + } + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + if(ids == null && type == null && olderThan == null) { + throw new InvalidParameterValueException("either ids, type or enddate must be specified"); + } + boolean result = _mgr.deleteEvents(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Unable to delete Events, one or more parameters has invalid values"); + } + } +} diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index dd0c3f894fd..5018236e5e2 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -218,9 +218,13 @@ listZones=15 #### events commands listEvents=15 listEventTypes=15 +archiveEvents=15 +deleteEvents=15 #### alerts commands listAlerts=3 +archiveAlerts=1 +deleteAlerts=1 #### system capacity commands listCapacity=3 diff --git a/core/src/com/cloud/alert/AlertVO.java b/core/src/com/cloud/alert/AlertVO.java index f6089d65043..3f014aa2b1f 100755 --- a/core/src/com/cloud/alert/AlertVO.java +++ b/core/src/com/cloud/alert/AlertVO.java @@ -28,9 +28,7 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; -import org.apache.cloudstack.api.Identity; import com.cloud.utils.db.GenericDao; -import org.apache.cloudstack.api.InternalIdentity; @Entity @Table(name="alert") @@ -68,16 +66,19 @@ public class AlertVO implements Alert { @Temporal(TemporalType.TIMESTAMP) @Column(name="resolved", updatable=true, nullable=true) private Date resolved; - + @Column(name="uuid") private String uuid; + @Column(name="archived") + private boolean archived; + public AlertVO() { - this.uuid = UUID.randomUUID().toString(); + this.uuid = UUID.randomUUID().toString(); } public AlertVO(Long id) { this.id = id; - this.uuid = UUID.randomUUID().toString(); + this.uuid = UUID.randomUUID().toString(); } @Override @@ -103,12 +104,12 @@ public class AlertVO implements Alert { } public Long getClusterId() { - return clusterId; - } - public void setClusterId(Long clusterId) { - this.clusterId = clusterId; - } - @Override + return clusterId; + } + public void setClusterId(Long clusterId) { + this.clusterId = clusterId; + } + @Override public Long getPodId() { return podId; } @@ -164,10 +165,19 @@ public class AlertVO implements Alert { @Override public String getUuid() { - return this.uuid; + return this.uuid; } - + public void setUuid(String uuid) { - this.uuid = uuid; + this.uuid = uuid; + } + + @Override + public boolean getArchived() { + return archived; + } + + public void setArchived(Boolean archived) { + this.archived = archived; } } diff --git a/core/src/com/cloud/event/EventVO.java b/core/src/com/cloud/event/EventVO.java index ac46f24b2ee..2c30eadebdc 100644 --- a/core/src/com/cloud/event/EventVO.java +++ b/core/src/com/cloud/event/EventVO.java @@ -29,74 +29,75 @@ import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Transient; -import org.apache.cloudstack.api.Identity; import com.cloud.utils.db.GenericDao; -import org.apache.cloudstack.api.InternalIdentity; @Entity @Table(name="event") public class EventVO implements Event { - @Id + @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="id") - private long id = -1; + private long id = -1; - @Column(name="type") - private String type; - - @Enumerated(value=EnumType.STRING) - @Column(name="state") + @Column(name="type") + private String type; + + @Enumerated(value=EnumType.STRING) + @Column(name="state") private State state = State.Completed; - @Column(name="description", length=1024) - private String description; + @Column(name="description", length=1024) + private String description; - @Column(name=GenericDao.CREATED_COLUMN) - private Date createDate; + @Column(name=GenericDao.CREATED_COLUMN) + private Date createDate; @Column(name="user_id") private long userId; - @Column(name="account_id") - private long accountId; + @Column(name="account_id") + private long accountId; @Column(name="domain_id") private long domainId; - @Column(name="level") - private String level = LEVEL_INFO; - - @Column(name="start_id") + @Column(name="level") + private String level = LEVEL_INFO; + + @Column(name="start_id") private long startId; - @Column(name="parameters", length=1024) - private String parameters; - - @Column(name="uuid") - private String uuid; + @Column(name="parameters", length=1024) + private String parameters; - @Transient - private int totalSize; + @Column(name="uuid") + private String uuid; - public static final String LEVEL_INFO = "INFO"; - public static final String LEVEL_WARN = "WARN"; - public static final String LEVEL_ERROR = "ERROR"; - - public EventVO() { - this.uuid = UUID.randomUUID().toString(); - } - - public long getId() { - return id; - } - @Override + @Column(name="archived") + private boolean archived; + + @Transient + private int totalSize; + + public static final String LEVEL_INFO = "INFO"; + public static final String LEVEL_WARN = "WARN"; + public static final String LEVEL_ERROR = "ERROR"; + + public EventVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public long getId() { + return id; + } + @Override public String getType() { - return type; - } - public void setType(String type) { - this.type = type; - } - @Override + return type; + } + public void setType(String type) { + this.type = type; + } + @Override public State getState() { return state; } @@ -105,27 +106,27 @@ public class EventVO implements Event { this.state = state; } - @Override + @Override public String getDescription() { - return description; - } - public void setDescription(String description) { - this.description = description; - } - @Override + return description; + } + public void setDescription(String description) { + this.description = description; + } + @Override public Date getCreateDate() { - return createDate; - } - public void setCreatedDate(Date createdDate) { - createDate = createdDate; - } - @Override + return createDate; + } + public void setCreatedDate(Date createdDate) { + createDate = createdDate; + } + @Override public long getUserId() { - return userId; - } - public void setUserId(long userId) { - this.userId = userId; - } + return userId; + } + public void setUserId(long userId) { + this.userId = userId; + } @Override public long getAccountId() { return accountId; @@ -165,21 +166,29 @@ public class EventVO implements Event { this.startId = startId; } - @Override + @Override public String getParameters() { - return parameters; - } - public void setParameters(String parameters) { - this.parameters = parameters; - } - - @Override - public String getUuid() { - return this.uuid; - } - - public void setUuid(String uuid) { - this.uuid = uuid; - } + return parameters; + } + public void setParameters(String parameters) { + this.parameters = parameters; + } + @Override + public String getUuid() { + return this.uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public boolean getArchived() { + return archived; + } + + public void setArchived(Boolean archived) { + this.archived = archived; + } } diff --git a/core/src/com/cloud/event/dao/EventDao.java b/core/src/com/cloud/event/dao/EventDao.java index bfcb818f20f..da5f47a90b4 100644 --- a/core/src/com/cloud/event/dao/EventDao.java +++ b/core/src/com/cloud/event/dao/EventDao.java @@ -30,4 +30,9 @@ public interface EventDao extends GenericDao { public List listOlderEvents(Date oldTime); EventVO findCompletedEvent(long startId); + + public List listToArchiveOrDeleteEvents(List ids, String type, Date olderThan, Long accountId); + + public void archiveEvents(List events); + } diff --git a/core/src/com/cloud/event/dao/EventDaoImpl.java b/core/src/com/cloud/event/dao/EventDaoImpl.java index 44fbb030dcc..6ba59c56b0a 100644 --- a/core/src/com/cloud/event/dao/EventDaoImpl.java +++ b/core/src/com/cloud/event/dao/EventDaoImpl.java @@ -30,24 +30,34 @@ 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 com.cloud.utils.db.Transaction; +import com.cloud.utils.db.SearchCriteria.Op; @Component @Local(value={EventDao.class}) public class EventDaoImpl extends GenericDaoBase implements EventDao { - public static final Logger s_logger = Logger.getLogger(EventDaoImpl.class.getName()); - protected final SearchBuilder CompletedEventSearch; - - public EventDaoImpl () { - CompletedEventSearch = createSearchBuilder(); - CompletedEventSearch.and("state",CompletedEventSearch.entity().getState(),SearchCriteria.Op.EQ); - CompletedEventSearch.and("startId", CompletedEventSearch.entity().getStartId(), SearchCriteria.Op.EQ); - CompletedEventSearch.done(); - } + public static final Logger s_logger = Logger.getLogger(EventDaoImpl.class.getName()); + protected final SearchBuilder CompletedEventSearch; + protected final SearchBuilder ToArchiveOrDeleteEventSearch; - @Override - public List searchAllEvents(SearchCriteria sc, Filter filter) { - return listIncludingRemovedBy(sc, filter); - } + public EventDaoImpl () { + CompletedEventSearch = createSearchBuilder(); + CompletedEventSearch.and("state",CompletedEventSearch.entity().getState(),SearchCriteria.Op.EQ); + CompletedEventSearch.and("startId", CompletedEventSearch.entity().getStartId(), SearchCriteria.Op.EQ); + CompletedEventSearch.done(); + + ToArchiveOrDeleteEventSearch = createSearchBuilder(); + ToArchiveOrDeleteEventSearch.and("id", ToArchiveOrDeleteEventSearch.entity().getId(), Op.IN); + ToArchiveOrDeleteEventSearch.and("type", ToArchiveOrDeleteEventSearch.entity().getType(), Op.EQ); + ToArchiveOrDeleteEventSearch.and("accountId", ToArchiveOrDeleteEventSearch.entity().getAccountId(), Op.EQ); + ToArchiveOrDeleteEventSearch.and("createDateL", ToArchiveOrDeleteEventSearch.entity().getCreateDate(), Op.LT); + ToArchiveOrDeleteEventSearch.done(); + } + + @Override + public List searchAllEvents(SearchCriteria sc, Filter filter) { + return listIncludingRemovedBy(sc, filter); + } @Override public List listOlderEvents(Date oldTime) { @@ -55,9 +65,8 @@ public class EventDaoImpl extends GenericDaoBase implements Event SearchCriteria sc = createSearchCriteria(); sc.addAnd("createDate", SearchCriteria.Op.LT, oldTime); return listIncludingRemovedBy(sc, null); - } - + @Override public EventVO findCompletedEvent(long startId) { SearchCriteria sc = CompletedEventSearch.create(); @@ -65,4 +74,36 @@ public class EventDaoImpl extends GenericDaoBase implements Event sc.setParameters("startId", startId); return findOneIncludingRemovedBy(sc); } + + @Override + public List listToArchiveOrDeleteEvents(List ids, String type, Date olderThan, Long accountId) { + SearchCriteria sc = ToArchiveOrDeleteEventSearch.create(); + if (ids != null) { + sc.setParameters("id", ids.toArray(new Object[ids.size()])); + } + if (type != null) { + sc.setParameters("type", type); + } + if (olderThan != null) { + sc.setParameters("createDateL", olderThan); + } + if (accountId != null) { + sc.setParameters("accountId", accountId); + } + return search(sc, null); + } + + @Override + public void archiveEvents(List events) { + + Transaction txn = Transaction.currentTxn(); + txn.start(); + for (EventVO event : events) { + event = lockRow(event.getId(), true); + event.setArchived(true); + update(event.getId(), event); + txn.commit(); + } + txn.close(); + } } diff --git a/server/src/com/cloud/alert/dao/AlertDao.java b/server/src/com/cloud/alert/dao/AlertDao.java index eb1faa51a2b..fda814d051d 100755 --- a/server/src/com/cloud/alert/dao/AlertDao.java +++ b/server/src/com/cloud/alert/dao/AlertDao.java @@ -16,6 +16,9 @@ // under the License. package com.cloud.alert.dao; +import java.util.Date; +import java.util.List; + import com.cloud.alert.AlertVO; import com.cloud.utils.db.GenericDao; @@ -23,4 +26,8 @@ public interface AlertDao extends GenericDao { AlertVO getLastAlert(short type, long dataCenterId, Long podId, Long clusterId); // This is for backward compatibility AlertVO getLastAlert(short type, long dataCenterId, Long podId); + + public boolean deleteAlert(List Ids, String type, Date olderThan, Long zoneId); + public boolean archiveAlert(List Ids, String type, Date olderThan, Long zoneId); + public List listOlderAlerts(Date oldTime); } diff --git a/server/src/com/cloud/alert/dao/AlertDaoImpl.java b/server/src/com/cloud/alert/dao/AlertDaoImpl.java index 2f3be882edd..4b9bc6a2988 100755 --- a/server/src/com/cloud/alert/dao/AlertDaoImpl.java +++ b/server/src/com/cloud/alert/dao/AlertDaoImpl.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.alert.dao; +import java.util.Date; import java.util.List; import javax.ejb.Local; @@ -25,11 +26,26 @@ import org.springframework.stereotype.Component; import com.cloud.alert.AlertVO; 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 com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.Transaction; @Component @Local(value = { AlertDao.class }) public class AlertDaoImpl extends GenericDaoBase implements AlertDao { + + protected final SearchBuilder AlertSearchByIdsAndType; + + public AlertDaoImpl() { + AlertSearchByIdsAndType = createSearchBuilder(); + AlertSearchByIdsAndType.and("id", AlertSearchByIdsAndType.entity().getId(), Op.IN); + AlertSearchByIdsAndType.and("type", AlertSearchByIdsAndType.entity().getType(), Op.EQ); + AlertSearchByIdsAndType.and("createdDateL", AlertSearchByIdsAndType.entity().getCreatedDate(), Op.LT); + AlertSearchByIdsAndType.and("data_center_id", AlertSearchByIdsAndType.entity().getDataCenterId(), Op.EQ); + AlertSearchByIdsAndType.done(); + } + @Override public AlertVO getLastAlert(short type, long dataCenterId, Long podId, Long clusterId) { Filter searchFilter = new Filter(AlertVO.class, "createdDate", Boolean.FALSE, Long.valueOf(0), Long.valueOf(1)); @@ -68,4 +84,73 @@ public class AlertDaoImpl extends GenericDaoBase implements Alert } return null; } + + @Override + public boolean archiveAlert(List Ids, String type, Date olderThan, Long zoneId) { + SearchCriteria sc = AlertSearchByIdsAndType.create(); + + if (Ids != null) { + sc.setParameters("id", Ids.toArray(new Object[Ids.size()])); + } + if(type != null) { + sc.setParameters("type", type); + } + if(zoneId != null) { + sc.setParameters("data_center_id", zoneId); + } + if(olderThan != null) { + sc.setParameters("createdDateL", olderThan); + } + boolean result = true;; + List alerts = listBy(sc); + if (Ids != null && alerts.size() < Ids.size()) { + result = false; + return result; + } + Transaction txn = Transaction.currentTxn(); + txn.start(); + for (AlertVO alert : alerts) { + alert = lockRow(alert.getId(), true); + alert.setArchived(true); + update(alert.getId(), alert); + txn.commit(); + } + txn.close(); + return result; + } + + @Override + public boolean deleteAlert(List ids, String type, Date olderThan, Long zoneId) { + SearchCriteria sc = AlertSearchByIdsAndType.create(); + + if (ids != null) { + sc.setParameters("id", ids.toArray(new Object[ids.size()])); + } + if(type != null) { + sc.setParameters("type", type); + } + if(zoneId != null) { + sc.setParameters("data_center_id", zoneId); + } + if(olderThan != null) { + sc.setParameters("createdDateL", olderThan); + } + boolean result = true; + List alerts = listBy(sc); + if (ids != null && alerts.size() < ids.size()) { + result = false; + return result; + } + remove(sc); + return result; + } + + @Override + public List listOlderAlerts(Date oldTime) { + if (oldTime == null) return null; + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("createDate", SearchCriteria.Op.LT, oldTime); + return listIncludingRemovedBy(sc, null); + } + } diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java index 764b3aeceaa..f7a32364cf8 100755 --- a/server/src/com/cloud/api/ApiDispatcher.java +++ b/server/src/com/cloud/api/ApiDispatcher.java @@ -50,6 +50,8 @@ import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.Validate; +import org.apache.cloudstack.api.command.user.event.ArchiveEventsCmd; +import org.apache.cloudstack.api.command.user.event.DeleteEventsCmd; import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -391,7 +393,7 @@ public class ApiDispatcher { // This piece of code is for maintaining backward compatibility // and support both the date formats(Bug 9724) // Do the date messaging for ListEventsCmd only - if (cmdObj instanceof ListEventsCmd) { + if (cmdObj instanceof ListEventsCmd || cmdObj instanceof DeleteEventsCmd || cmdObj instanceof ArchiveEventsCmd) { boolean isObjInNewDateFormat = isObjInNewDateFormat(paramObj.toString()); if (isObjInNewDateFormat) { DateFormat newFormat = BaseCmd.NEW_INPUT_FORMAT; @@ -406,6 +408,8 @@ public class ApiDispatcher { date = messageDate(date, 0, 0, 0); } else if (field.getName().equals("endDate")) { date = messageDate(date, 23, 59, 59); + } else if (field.getName().equals("olderThan")) { + date = messageDate(date, 0, 0, 0); } field.set(cmdObj, date); } diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 8d8663a4010..35fe2f35275 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -397,6 +397,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sb.and("state", sb.entity().getState(), SearchCriteria.Op.NEQ); sb.and("startId", sb.entity().getStartId(), SearchCriteria.Op.EQ); sb.and("createDate", sb.entity().getCreateDate(), SearchCriteria.Op.BETWEEN); + sb.and("archived", sb.entity().getArchived(), SearchCriteria.Op.EQ); SearchCriteria sc = sb.create(); // building ACL condition @@ -430,6 +431,8 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sc.setParameters("createDateL", endDate); } + sc.setParameters("archived", false); + Pair, Integer> eventPair = null; // event_view will not have duplicate rows for each event, so searchAndCount should be good enough. if ((entryTime != null) && (duration != null)) { diff --git a/server/src/com/cloud/api/query/vo/EventJoinVO.java b/server/src/com/cloud/api/query/vo/EventJoinVO.java index f29a942a59f..12d7e5ae4d0 100644 --- a/server/src/com/cloud/api/query/vo/EventJoinVO.java +++ b/server/src/com/cloud/api/query/vo/EventJoinVO.java @@ -104,6 +104,8 @@ public class EventJoinVO extends BaseViewVO implements ControlledViewEntity { @Column(name="project_name") private String projectName; + @Column(name="archived") + private boolean archived; public EventJoinVO() { @@ -313,5 +315,12 @@ public class EventJoinVO extends BaseViewVO implements ControlledViewEntity { this.parameters = parameters; } + public boolean getArchived() { + return archived; + } + + public void setArchived(Boolean archived) { + this.archived = archived; + } } diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 418f97d8c71..64465a2034c 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -204,9 +204,10 @@ public enum Config { SecStorageSessionMax("Advanced", AgentManager.class, Integer.class, "secstorage.session.max", "50", "The max number of command execution sessions that a SSVM can handle", null), SecStorageCmdExecutionTimeMax("Advanced", AgentManager.class, Integer.class, "secstorage.cmd.execution.time.max", "30", "The max command execution time in minute", null), SecStorageProxy("Advanced", AgentManager.class, String.class, "secstorage.proxy", null, "http proxy used by ssvm, in http://username:password@proxyserver:port format", null), + AlertPurgeInterval("Advanced", ManagementServer.class, Integer.class, "alert.purge.interval", "86400", "The interval (in seconds) to wait before running the alert purge thread", null), + AlertPurgeDelay("Advanced", ManagementServer.class, Integer.class, "alert.purge.delay", "0", "Alerts older than specified number days will be purged. Set this value to 0 to never delete alerts", null), - - DirectAttachNetworkEnabled("Advanced", ManagementServer.class, Boolean.class, "direct.attach.network.externalIpAllocator.enabled", "false", "Direct-attach VMs using external DHCP server", "true,false"), + DirectAttachNetworkEnabled("Advanced", ManagementServer.class, Boolean.class, "direct.attach.network.externalIpAllocator.enabled", "false", "Direct-attach VMs using external DHCP server", "true,false"), DirectAttachNetworkExternalAPIURL("Advanced", ManagementServer.class, String.class, "direct.attach.network.externalIpAllocator.url", null, "Direct-attach VMs using external DHCP server (API url)", null), CheckPodCIDRs("Advanced", ManagementServer.class, String.class, "check.pod.cidrs", "true", "If true, different pods must belong to different CIDR subnets.", "true,false"), NetworkGcWait("Advanced", ManagementServer.class, Integer.class, "network.gc.wait", "600", "Time (in seconds) to wait before shutting down a network that's not in used", null), diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 11400de7328..d70c45f1f8a 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -47,6 +47,7 @@ import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.naming.ConfigurationException; +import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ApiConstants; @@ -125,6 +126,7 @@ import com.cloud.alert.AlertManager; import com.cloud.alert.AlertVO; import com.cloud.alert.dao.AlertDao; import com.cloud.api.ApiDBUtils; +import com.cloud.api.query.vo.EventJoinVO; import com.cloud.async.AsyncJobExecutor; import com.cloud.async.AsyncJobManager; import com.cloud.async.AsyncJobResult; @@ -188,6 +190,7 @@ import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.info.ConsoleProxyInfo; import com.cloud.keystore.KeystoreManager; import com.cloud.network.IpAddress; +import com.cloud.network.as.ConditionVO; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.LoadBalancerDao; @@ -262,6 +265,7 @@ import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.JoinBuilder.JoinType; +import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; @@ -295,7 +299,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe public static final Logger s_logger = Logger.getLogger(ManagementServerImpl.class.getName()); @Inject - private AccountManager _accountMgr; + public AccountManager _accountMgr; @Inject private AgentManager _agentMgr; @Inject @@ -311,7 +315,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private SecondaryStorageVmDao _secStorageVmDao; @Inject - private EventDao _eventDao; + public EventDao _eventDao; @Inject private DataCenterDao _dcDao; @Inject @@ -347,7 +351,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private AccountDao _accountDao; @Inject - private AlertDao _alertDao; + public AlertDao _alertDao; @Inject private CapacityDao _capacityDao; @Inject @@ -371,6 +375,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private AsyncJobManager _asyncMgr; private int _purgeDelay; + private int _alertPurgeDelay; @Inject private InstanceGroupDao _vmGroupDao; @Inject @@ -417,6 +422,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe EventUtils _forceEventUtilsRef; */ private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker")); + private final ScheduledExecutorService _alertExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AlertChecker")); private KeystoreManager _ksMgr; private Map _configs; @@ -446,6 +452,15 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe _eventExecutor.scheduleAtFixedRate(new EventPurgeTask(), cleanup, cleanup, TimeUnit.SECONDS); } + //Alerts purge configurations + int alertPurgeInterval = NumbersUtil.parseInt(_configDao.getValue(Config.AlertPurgeInterval.key()), + 60 * 60 * 24); // 1 day. + _alertPurgeDelay = NumbersUtil.parseInt(_configDao.getValue(Config.AlertPurgeDelay.key()), 0); + if (_alertPurgeDelay != 0) { + _alertExecutor.scheduleAtFixedRate(new AlertPurgeTask(), alertPurgeInterval, alertPurgeInterval, + TimeUnit.SECONDS); + } + String[] availableIds = TimeZone.getAvailableIDs(); _availableIdsMap = new HashMap(availableIds.length); for (String id : availableIds) { @@ -538,6 +553,42 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe return _eventDao.search(sc, null); } + @Override + public boolean archiveEvents(ArchiveEventsCmd cmd) { + List ids = cmd.getIds(); + boolean result =true; + + List events = _eventDao.listToArchiveOrDeleteEvents(ids, cmd.getType(), cmd.getOlderThan(), cmd.getEntityOwnerId()); + ControlledEntity[] sameOwnerEvents = events.toArray(new ControlledEntity[events.size()]); + _accountMgr.checkAccess(UserContext.current().getCaller(), null, true, sameOwnerEvents); + + if (ids != null && events.size() < ids.size()) { + result = false; + return result; + } + _eventDao.archiveEvents(events); + return result; + } + + @Override + public boolean deleteEvents(DeleteEventsCmd cmd) { + List ids = cmd.getIds(); + boolean result =true; + + List events = _eventDao.listToArchiveOrDeleteEvents(ids, cmd.getType(), cmd.getOlderThan(), cmd.getEntityOwnerId()); + ControlledEntity[] sameOwnerEvents = events.toArray(new ControlledEntity[events.size()]); + _accountMgr.checkAccess(UserContext.current().getCaller(), null, true, sameOwnerEvents); + + if (ids != null && events.size() < ids.size()) { + result = false; + return result; + } + for (EventVO event : events) { + _eventDao.remove(event.getId()); + } + return result; + } + private Date massageDate(Date date, int hourOfDay, int minute, int second) { Calendar cal = Calendar.getInstance(); cal.setTime(date); @@ -1663,10 +1714,25 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe sc.addAnd("type", SearchCriteria.Op.EQ, type); } + sc.addAnd("archived", SearchCriteria.Op.EQ, false); Pair, Integer> result = _alertDao.searchAndCount(sc, searchFilter); return new Pair, Integer>(result.first(), result.second()); } + @Override + public boolean archiveAlerts(ArchiveAlertsCmd cmd) { + Long zoneId = _accountMgr.checkAccessAndSpecifyAuthority(UserContext.current().getCaller(), null); + boolean result = _alertDao.archiveAlert(cmd.getIds(), cmd.getType(), cmd.getOlderThan(), zoneId); + return result; + } + + @Override + public boolean deleteAlerts(DeleteAlertsCmd cmd) { + Long zoneId = _accountMgr.checkAccessAndSpecifyAuthority(UserContext.current().getCaller(), null); + boolean result = _alertDao.deleteAlert(cmd.getIds(), cmd.getType(), cmd.getOlderThan(), zoneId); + return result; + } + @Override public List listTopConsumedResources(ListCapacityCmd cmd) { @@ -2168,6 +2234,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(AddIpToVmNicCmd.class); cmdList.add(RemoveIpFromVmNicCmd.class); cmdList.add(ListNicsCmd.class); + cmdList.add(ArchiveAlertsCmd.class); + cmdList.add(DeleteAlertsCmd.class); + cmdList.add(ArchiveEventsCmd.class); + cmdList.add(DeleteEventsCmd.class); return cmdList; } @@ -2205,6 +2275,39 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } } + protected class AlertPurgeTask implements Runnable { + @Override + public void run() { + try { + GlobalLock lock = GlobalLock.getInternLock("AlertPurge"); + if (lock == null) { + s_logger.debug("Couldn't get the global lock"); + return; + } + if (!lock.lock(30)) { + s_logger.debug("Couldn't lock the db"); + return; + } + try { + final Calendar purgeCal = Calendar.getInstance(); + purgeCal.add(Calendar.DAY_OF_YEAR, - _alertPurgeDelay); + Date purgeTime = purgeCal.getTime(); + s_logger.debug("Deleting alerts older than: " + purgeTime.toString()); + List oldAlerts = _alertDao.listOlderAlerts(purgeTime); + s_logger.debug("Found " + oldAlerts.size() + " events to be purged"); + for (AlertVO alert : oldAlerts) { + _alertDao.expunge(alert.getId()); + } + } catch (Exception e) { + s_logger.error("Exception ", e); + } finally { + lock.unlock(); + } + } catch (Exception e) { + s_logger.error("Exception ", e); + } + } + } @Override public Pair, Integer> searchForStoragePools(Criteria c) { diff --git a/server/test/com/cloud/alert/AlertControlsUnitTest.java b/server/test/com/cloud/alert/AlertControlsUnitTest.java new file mode 100644 index 00000000000..412a2619840 --- /dev/null +++ b/server/test/com/cloud/alert/AlertControlsUnitTest.java @@ -0,0 +1,67 @@ +package com.cloud.alert; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyList; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +import java.util.Date; + +import junit.framework.TestCase; + +import org.apache.log4j.Logger; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +import com.cloud.alert.dao.AlertDao; +import com.cloud.server.ManagementServerImpl; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; + +public class AlertControlsUnitTest extends TestCase { + private static final Logger s_logger = Logger.getLogger(AlertControlsUnitTest.class); + + @Spy ManagementServerImpl _mgmtServer = new ManagementServerImpl(); + @Mock AccountManager _accountMgr; + @Mock AlertDao _alertDao; + @Override + @Before + protected void setUp() { + MockitoAnnotations.initMocks(this); + _mgmtServer._alertDao = _alertDao; + _mgmtServer._accountMgr = _accountMgr; + doReturn(3L).when(_accountMgr).checkAccessAndSpecifyAuthority(any(Account.class), anyLong()); + when(_alertDao.archiveAlert(anyList(), anyString(), any(Date.class), anyLong())).thenReturn(true); + when(_alertDao.deleteAlert(anyList(), anyString(), any(Date.class), anyLong())).thenReturn(true); + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testInjected() throws Exception { + s_logger.info("Starting test to archive and delete alerts"); + archiveAlerts(); + deleteAlerts(); + s_logger.info("archive/delete alerts: TEST PASSED"); + } + + protected void archiveAlerts() { + // archive alerts + String msg = "Archive Alerts: TEST FAILED"; + assertNotNull(msg, _mgmtServer._alertDao.archiveAlert(null, "system alert",null, 2L)); + } + + protected void deleteAlerts() { + // delete alerts + String msg = "Delete Alerts: TEST FAILED"; + assertNotNull(msg, _mgmtServer._alertDao.deleteAlert(null, "system alert",null, 2L)); + } +} diff --git a/server/test/com/cloud/event/EventControlsUnitTest.java b/server/test/com/cloud/event/EventControlsUnitTest.java new file mode 100644 index 00000000000..3fe47471e14 --- /dev/null +++ b/server/test/com/cloud/event/EventControlsUnitTest.java @@ -0,0 +1,68 @@ +package com.cloud.event; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyList; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; + +import java.util.Date; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.log4j.Logger; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +import com.cloud.event.dao.EventDao; +import com.cloud.server.ManagementServerImpl; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; + +public class EventControlsUnitTest extends TestCase{ + private static final Logger s_logger = Logger.getLogger(EventControlsUnitTest.class); + + @Spy ManagementServerImpl _mgmtServer = new ManagementServerImpl(); + @Mock AccountManager _accountMgr; + @Mock EventDao _eventDao; + List _events = null; + + @Override + @Before + protected void setUp() { + MockitoAnnotations.initMocks(this); + _mgmtServer._eventDao = _eventDao; + _mgmtServer._accountMgr = _accountMgr; + doNothing().when(_accountMgr).checkAccess(any(Account.class), any(AccessType.class), any(Boolean.class), any(ControlledEntity.class)); + when(_eventDao.listToArchiveOrDeleteEvents(anyList(), anyString(), any(Date.class), anyLong())).thenReturn(_events); + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testInjected() throws Exception { + s_logger.info("Starting test to archive and delete events"); + archiveEvents(); + deleteEvents(); + s_logger.info("archive/delete events: TEST PASSED"); + } + + protected void archiveEvents() { + // archive alerts + doNothing().when(_eventDao).archiveEvents(_events); + } + + protected void deleteEvents() { + // delete alerts + } +} diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index f3112a1da27..ca15bdaf781 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -140,3 +140,48 @@ CREATE TABLE nic_secondary_ips ( ALTER TABLE `cloud`.`nics` ADD COLUMN secondary_ip SMALLINT DEFAULT '0' COMMENT 'secondary ips configured for the nic'; ALTER TABLE `cloud`.`user_ip_address` ADD COLUMN dnat_vmip VARCHAR(40); + +ALTER TABLE `cloud`.`alert` ADD COLUMN `archived` tinyint(1) unsigned NOT NULL DEFAULT 0; +ALTER TABLE `cloud`.`event` ADD COLUMN `archived` tinyint(1) unsigned NOT NULL DEFAULT 0; +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'alert.purge.interval', '86400', 'The interval (in seconds) to wait before running the alert purge thread'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'alert.purge.delay', '0', 'Alerts older than specified number days will be purged. Set this value to 0 to never delete alerts'); + +DROP VIEW IF EXISTS `cloud`.`event_view`; +CREATE VIEW `cloud`.`event_view` AS + select + event.id, + event.uuid, + event.type, + event.state, + event.description, + event.created, + event.level, + event.parameters, + event.start_id, + eve.uuid start_uuid, + event.user_id, + event.archived, + user.username user_name, + account.id account_id, + account.uuid account_uuid, + account.account_name account_name, + account.type account_type, + domain.id domain_id, + domain.uuid domain_uuid, + domain.name domain_name, + domain.path domain_path, + projects.id project_id, + projects.uuid project_uuid, + projects.name project_name + from + `cloud`.`event` + inner join + `cloud`.`account` ON event.account_id = account.id + inner join + `cloud`.`domain` ON event.domain_id = domain.id + inner join + `cloud`.`user` ON event.user_id = user.id + left join + `cloud`.`projects` ON projects.project_account_id = event.account_id + left join + `cloud`.`event` eve ON event.start_id = eve.id;