/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.db.client.model.util;
import java.net.URI;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.constraint.Constraint;
import com.emc.storageos.db.client.constraint.ContainmentConstraint;
import com.emc.storageos.db.client.constraint.URIQueryResultList;
import com.emc.storageos.db.client.model.ActionableEvent;
import com.emc.storageos.db.client.model.DataObject;
import com.emc.storageos.db.client.model.NamedURI;
import com.emc.storageos.db.client.model.StringSet;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.google.common.collect.Lists;
public class EventUtils {
private static Logger log = LoggerFactory.getLogger(EventUtils.class);
public static String hostVcenterUnassign = "hostVcenterUnassign";
public static String hostVcenterChange = "hostVcenterChange";
public static String hostDatacenterChange = "hostDatacenterChange";
public static String hostClusterChange = "hostClusterChange";
public static String removeInitiator = "removeInitiator";
public static String addInitiator = "addInitiator";
public static String hostVcenterUnassignDecline = "hostVcenterUnassignDecline";
public static String hostVcenterChangeDecline = "hostVcenterChangeDecline";
public static String hostDatacenterChangeDecline = "hostDatacenterChangeDecline";
public static String hostClusterChangeDecline = "hostClusterChangeDecline";
public static String removeInitiatorDecline = "removeInitiatorDecline";
public static String addInitiatorDecline = "addInitiatorDecline";
private static List<EventCode> ALLOWED_DUPLICATE_EVENTS = Lists.newArrayList(EventCode.HOST_INITIATOR_ADD,
EventCode.HOST_INITIATOR_DELETE);
public enum EventCode {
HOST_CLUSTER_CHANGE("101"),
HOST_INITIATOR_ADD("102"),
HOST_INITIATOR_DELETE("103"),
HOST_DATACENTER_CHANGE("104"),
HOST_VCENTER_CHANGE("105"),
UNASSIGN_HOST_FROM_VCENTER("106");
private String code;
EventCode(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
/**
* Creates an actionable event and persists to the database
*
* @param dbClient db client
* @param eventCode the code for the event
* @param tenant the tenant that owns the event
* @param name the name of the event
* @param description the description of what the event will do
* @param warning the warning message to display to the user if the event will cause data loss or other impacting change
* @param resource the resource that owns the event (host, cluster, etc)
* @param affectedResources the resources that are affected by this event (host, cluster, initiator, etc)
* @param approveMethod the method to invoke when approving the event
* @param approveParameters the parameters to pass to the approve method
* @param declineMethod the method to invoke when declining the event
* @param declineParameters the parameters to pass to the decline method
*/
public static void createActionableEvent(DbClient dbClient, EventUtils.EventCode eventCode, URI tenant, String name, String description,
String warning,
DataObject resource, Collection<URI> affectedResources, String approveMethod, Object[] approveParameters,
String declineMethod, Object[] declineParameters) {
ActionableEvent duplicateEvent = null;
if (ALLOWED_DUPLICATE_EVENTS.contains(eventCode)) {
duplicateEvent = getDuplicateEvent(dbClient, eventCode.getCode(), resource.getId(), affectedResources);
} else {
duplicateEvent = getDuplicateEvent(dbClient, eventCode.getCode(), resource.getId(), null);
}
if (duplicateEvent != null) {
log.info("Duplicate event " + duplicateEvent.getId() + " is already in a pending state for resource " + resource.getId()
+ ". Will not create a new event");
duplicateEvent.setCreationTime(Calendar.getInstance());
duplicateEvent.setDescription(description);
duplicateEvent.setWarning(warning);
duplicateEvent.setAffectedResources(getAffectedResources(affectedResources));
setEventMethods(duplicateEvent, approveMethod, approveParameters, declineMethod, declineParameters);
dbClient.updateObject(duplicateEvent);
} else {
ActionableEvent event = new ActionableEvent();
event.setEventCode(eventCode.getCode());
event.setId(URIUtil.createId(ActionableEvent.class));
event.setTenant(tenant);
event.setDescription(description);
event.setWarning(warning);
event.setEventStatus(ActionableEvent.Status.pending.name());
event.setResource(new NamedURI(resource.getId(), resource.getLabel()));
event.setAffectedResources(getAffectedResources(affectedResources));
setEventMethods(event, approveMethod, approveParameters, declineMethod, declineParameters);
event.setLabel(name);
dbClient.createObject(event);
log.info("Created Actionable Event: " + event.getId() + " Tenant: " + event.getTenant() + " Description: "
+ event.getDescription() + " Warning: " + event.getWarning()
+ " Event Status: " + event.getEventStatus() + " Resource: " + event.getResource() + " Event Code: "
+ event.getEventCode()
+ " Approve Method: " + approveMethod
+ " Decline Method: " + declineMethod);
}
}
/**
* Creates an actionable event and persists to the database
*
* @param dbClient db client
* @param eventCode the code for the event
* @param tenant the tenant that owns the event
* @param name the name of the event
* @param description the description of what the event will do
* @param warning the warning message to display to the user if the event will cause data loss or other impacting change
* @param resource the resource that owns the event (host, cluster, etc)
* @param affectedResources the resources that are affected by this event (host, cluster, initiator, etc)
* @param approveMethod the method to invoke when approving the event
* @param approveParameters the parameters to pass to the approve method
*/
public static void createActionableEvent(DbClient dbClient, EventUtils.EventCode eventCode, URI tenant, String name, String description,
String warning,
DataObject resource, List<URI> affectedResources, String approveMethod, Object[] approveParameters) {
createActionableEvent(dbClient, eventCode, tenant, name, description, warning,
resource, affectedResources, approveMethod, approveParameters, null, null);
}
/**
* Returns a duplicate pending event if one exists, else returns null
*
* @param dbClient db client
* @param the event code
* @param resourceId the id of the resource to check
* @param affectedResources list of affected resources to check or null to skip the check
* @return event if a duplicate is found, else null
*/
public static ActionableEvent getDuplicateEvent(DbClient dbClient, String eventCode, URI resourceId,
Collection<URI> affectedResources) {
List<ActionableEvent> events = findResourceEvents(dbClient, resourceId);
for (ActionableEvent event : events) {
if ((event.getEventStatus().equalsIgnoreCase(ActionableEvent.Status.pending.name())
|| event.getEventStatus().equalsIgnoreCase(ActionableEvent.Status.failed.name()))
&& event.getEventCode().equalsIgnoreCase(eventCode)
&& (affectedResources == null || event.getAffectedResources().equals(getAffectedResources(affectedResources)))) {
return event;
}
}
return null;
}
/**
* Delete all actionable events for a given resource
*
* @param dbClient db client
* @param resourceId the resource id for events to delete
*/
public static void deleteResourceEvents(DbClient dbClient, URI resourceId) {
List<ActionableEvent> events = findResourceEvents(dbClient, resourceId);
log.info("Deleting actionable events for resource " + resourceId);
for (ActionableEvent event : events) {
log.info("Deleting Actionable Event: " + event.getId() + " Tenant: " + event.getTenant() + " Description: "
+ event.getDescription() + " Warning: " + event.getWarning()
+ " Event Status: " + event.getEventStatus() + " Resource: " + event.getResource() + " Event Code: "
+ event.getEventCode());
dbClient.markForDeletion(event);
}
}
/**
* Serialize and set the approve and decline methods for an event
*
* @param event
* @param approveMethod
* @param approveParameters
* @param declineMethod
* @param declineParameters
*/
private static void setEventMethods(ActionableEvent event, String approveMethod, Object[] approveParameters, String declineMethod,
Object[] declineParameters) {
if (approveMethod != null) {
com.emc.storageos.db.client.model.ActionableEvent.Method method = new ActionableEvent.Method(
approveMethod, approveParameters);
event.setApproveMethod(method.serialize());
}
if (declineMethod != null) {
com.emc.storageos.db.client.model.ActionableEvent.Method method = new ActionableEvent.Method(
declineMethod, declineParameters);
event.setDeclineMethod(method.serialize());
}
}
/**
* Create affected resources string set with all non-null URIs
*
* @param ids the resource ids
* @return stringset of non-null ids
*/
public static StringSet getAffectedResources(Collection<URI> ids) {
StringSet result = new StringSet();
for (URI uri : ids) {
if (!NullColumnValueGetter.isNullURI(uri)) {
result.add(uri.toString());
}
}
return result;
}
public static List<ActionableEvent> findResourceEvents(DbClient dbClient, URI resourceId) {
return getEvents(dbClient, ContainmentConstraint.Factory.getResourceEventConstraint(resourceId));
}
public static List<ActionableEvent> findAffectedResourceEvents(DbClient dbClient, URI resourceId) {
return getEvents(dbClient, ContainmentConstraint.Factory.getAffectedResourceEventConstraint(resourceId));
}
/**
* Get list of pending events for an affected resource
*
* @param dbClient the dbclient
* @param resourceId the resource to search for in affected resources
* @return list of pending actionable events
*/
public static List<ActionableEvent> findAffectedResourcePendingEvents(DbClient dbClient, URI resourceId) {
List<ActionableEvent> events = findAffectedResourceEvents(dbClient, resourceId);
List<ActionableEvent> result = Lists.newArrayList();
for (ActionableEvent event : events) {
if (event != null && event.getEventStatus() != null
&& (event.getEventStatus().equalsIgnoreCase(ActionableEvent.Status.pending.name())
|| event.getEventStatus().equalsIgnoreCase(ActionableEvent.Status.failed.name()))) {
result.add(event);
}
}
return result;
}
private static List<ActionableEvent> getEvents(DbClient dbClient, Constraint constraint) {
URIQueryResultList results = new URIQueryResultList();
dbClient.queryByConstraint(constraint, results);
List<ActionableEvent> events = Lists.newArrayList();
Iterator<URI> it = results.iterator();
while (it.hasNext()) {
ActionableEvent event = dbClient.queryObject(ActionableEvent.class, it.next());
if (event != null) {
events.add(event);
}
}
return events;
}
}