/*
* Copyright (c) 2013-2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.systemservices.impl.resource;
import static com.emc.storageos.svcs.errorhandling.resources.ServiceCode.toServiceCode;
import static com.emc.storageos.svcs.errorhandling.resources.ServiceErrorFactory.toServiceErrorRestRep;
import java.net.URI;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.ws.rs.core.Response;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.emc.storageos.coordinator.client.service.CoordinatorClient.LicenseType;
import com.emc.storageos.coordinator.common.impl.ServiceImpl;
import com.emc.storageos.db.client.DbClient;
import com.emc.storageos.db.client.URIUtil;
import com.emc.storageos.db.client.model.DataObject;
import com.emc.storageos.db.client.model.NamedURI;
import com.emc.storageos.db.client.model.Operation;
import com.emc.storageos.db.client.model.SysEvent;
import com.emc.storageos.db.client.model.Task;
import com.emc.storageos.db.client.model.util.TaskUtils;
import com.emc.storageos.model.NamedRelatedResourceRep;
import com.emc.storageos.model.ResourceOperationTypeEnum;
import com.emc.storageos.model.RestLinkRep;
import com.emc.storageos.model.TaskResourceRep;
import com.emc.storageos.model.event.EventParameters;
import com.emc.storageos.security.audit.AuditLogManager;
import com.emc.storageos.security.authorization.BasePermissionsHelper;
import com.emc.storageos.services.OperationTypeEnum;
import com.emc.storageos.services.util.TimeUtils;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.svcs.errorhandling.resources.ForbiddenException;
import com.emc.storageos.systemservices.impl.eventhandler.connectemc.BuildEsrsDevice;
import com.emc.storageos.systemservices.impl.eventhandler.connectemc.CallHomeConstants;
import com.emc.storageos.systemservices.impl.eventhandler.connectemc.CallHomeEventManager;
import com.emc.storageos.systemservices.impl.eventhandler.connectemc.CallHomeEventsFacade;
import com.emc.storageos.systemservices.impl.eventhandler.connectemc.SendAlertEvent;
import com.emc.storageos.systemservices.impl.licensing.LicenseInfoExt;
import com.emc.storageos.systemservices.impl.licensing.LicenseInfoListExt;
import com.emc.storageos.systemservices.impl.licensing.LicenseManager;
import com.emc.storageos.systemservices.impl.upgrade.CoordinatorClientExt;
public class CallHomeServiceImpl extends BaseLogSvcResource implements CallHomeService {
private BuildEsrsDevice _buildEsrsDevice;
private CallHomeEventsFacade _callHomeEventsFacade;
private LicenseManager _licenseManager;
private CallHomeEventManager _callHomeEventManager;
private static final int FIXED_THREAD_POOL_SIZE = 10;
private static ExecutorService executorService = null;
@Autowired
private AuditLogManager _auditMgr;
@Autowired
private DbClient dbClient;
@Autowired
protected BasePermissionsHelper permissionsHelper = null;
@Autowired
private ServiceImpl serviceInfo;
@Autowired
private LogService logService;
@Autowired
CoordinatorClientExt coordinator;
private static final Logger _log = LoggerFactory.getLogger(CallHomeServiceImpl.class);
private static final String EVENT_SERVICE_TYPE = "callHome";
private synchronized static ExecutorService getExecutorServiceInstance() {
if (executorService == null) {
executorService = Executors.newFixedThreadPool(FIXED_THREAD_POOL_SIZE);
}
return executorService;
}
@Override
public TaskResourceRep sendInternalAlert(String source, int eventId, List<String> nodeIds, List<String> nodeNames,
List<String> logNames, int severity, String start, String end, String msgRegex, int maxCount,
EventParameters eventParameters) throws Exception {
_log.info("Sending internal alert for id: {} and source: {}", eventId, source);
return sendAlert(source, eventId, nodeIds, nodeNames, logNames, severity, start, end
, msgRegex, maxCount, true, 1, eventParameters);
}
@Override
public TaskResourceRep sendAlert(String source, int eventId, List<String> nodeIds, List<String> nodeNames,
List<String> logNames, int severity, String start, String end, String msgRegex, int maxCount,
boolean forceAttachLogs, int force, EventParameters eventParameters) throws Exception {
if (LogService.runningRequests.get() >= LogService.MAX_THREAD_COUNT) {
_log.info("Current running requests: {} vs maximum allowed {}",
LogService.runningRequests, LogService.MAX_THREAD_COUNT);
throw APIException.serviceUnavailable.logServiceIsBusy();
}
// if not configured for callhome, do not continue.
_callHomeEventManager.validateSendEvent();
// validate event id
if (!CallHomeConstants.VALID_ALERT_EVENT_IDS.contains(eventId)) {
throw APIException.badRequests.parameterIsNotOneOfAllowedValues("event_id",
CallHomeConstants.VALID_ALERT_EVENT_IDS.toString());
}
LicenseInfoExt licenseInfo = null;
// If the user specified a license type to use for sending alerts get it from coordinator
// Otherwise check if controller or unstructured is licensed
if (source != null && !source.isEmpty()) {
// validate source
LicenseType licenseType = LicenseType.findByValue(source);
if (licenseType == null) {
throw APIException.badRequests.parameterIsNotOneOfAllowedValues("source",
LicenseType.getValuesAsStrings().toString());
}
// get an instance of the requested license information
licenseInfo = _licenseManager.getLicenseInfoFromCoordinator(licenseType);
if (licenseInfo == null) {
throw APIException.internalServerErrors.licenseInfoNotFoundForType(licenseType.toString());
}
} else {
licenseInfo = _licenseManager.getLicenseInfoFromCoordinator(LicenseType.CONTROLLER);
if (licenseInfo == null) {
licenseInfo = _licenseManager.getLicenseInfoFromCoordinator(LicenseType.UNSTRUCTURED);
}
}
if (licenseInfo == null) {
throw ForbiddenException.forbidden.licenseNotFound(LicenseType.CONTROLLER.toString() + " or " +
LicenseType.UNSTRUCTURED.toString());
}
if (licenseInfo.isTrialLicense()) {
_log.warn("Cannot send alert to SYR for trial license {} ",
licenseInfo.getLicenseType().toString());
throw APIException.forbidden.permissionDeniedForTrialLicense(
licenseInfo.getLicenseType().toString());
}
// invoke get-logs api for the dry run
List<String> logNamesToUse = getLogNamesFromAlias(logNames);
try {
logService.getLogs(nodeIds, nodeNames, logNamesToUse, severity, start,
end, msgRegex, maxCount, true);
} catch (Exception e) {
_log.error("Failed to dry run get-logs, exception: {}", e);
throw e;
}
URI sysEventId = URIUtil.createId(SysEvent.class);
String opID = UUID.randomUUID().toString();
SendAlertEvent sendAlertEvent = new SendAlertEvent(serviceInfo,
dbClient, _logSvcPropertiesLoader, sysEventId, opID,
getMediaType(), licenseInfo, permissionsHelper, coordinator);
sendAlertEvent.setEventId(eventId);
sendAlertEvent.setNodeIds(nodeIds);
sendAlertEvent.setLogNames(logNamesToUse);
sendAlertEvent.setSeverity(severity);
sendAlertEvent.setStart(TimeUtils.getDateTimestamp(start));
sendAlertEvent.setEnd(TimeUtils.getDateTimestamp(end));
validateMsgRegex(msgRegex);
sendAlertEvent.setMsgRegex(msgRegex);
sendAlertEvent.setEventParameters(eventParameters);
sendAlertEvent.setMaxCount(maxCount);
sendAlertEvent.setForceAttachLogs(forceAttachLogs);
// Persisting this operation
Operation op = new Operation();
op.setName("SEND ALERT " + eventId);
op.setDescription("SEND ALERT EVENT code:" + eventId + ", severity:" + severity);
op.setResourceType(ResourceOperationTypeEnum.SYS_EVENT);
SysEvent sysEvent = createSysEventRecord(sysEventId, opID, op, force);
// Starting send event job
getExecutorServiceInstance().submit(sendAlertEvent);
auditCallhome(OperationTypeEnum.SEND_ALERT,
AuditLogManager.AUDITOP_BEGIN,
null, nodeIds, logNames, start, end);
return toTask(sysEvent, opID, op);
}
private TaskResourceRep toTask(DataObject resource, String taskId, Operation operation) {
// If the Operation has been serialized in this request, then it should have the corresponding task embedded in it
Task task = operation.getTask(resource.getId());
if (task != null) {
return toTask(task);
}
else {
// It wasn't recently serialized, so fallback to looking for the task in the DB
task = TaskUtils.findTaskForRequestId(dbClient, resource.getId(), taskId);
if (task != null) {
return toTask(task);
}
else {
throw new IllegalStateException(String.format(
"Task not found for resource %s, op %s in either the operation or the database", resource.getId(), taskId));
}
}
}
private TaskResourceRep toTask(Task task) {
TaskResourceRep taskResourceRep = new TaskResourceRep();
taskResourceRep.setId(task.getId());
NamedURI resource = task.getResource();
NamedRelatedResourceRep namedRelatedResourceRep = new NamedRelatedResourceRep(resource.getURI(),
new RestLinkRep("self", URI.create("/" + resource.getURI())), resource.getName());
taskResourceRep.setResource(namedRelatedResourceRep);
if (!StringUtils.isBlank(task.getRequestId())) {
taskResourceRep.setOpId(task.getRequestId());
}
// Operation
taskResourceRep.setState(task.getStatus());
if (task.getServiceCode() != null) {
taskResourceRep.setServiceError(toServiceErrorRestRep(toServiceCode(task.getServiceCode()),
task.getMessage()));
} else {
taskResourceRep.setMessage(task.getMessage());
}
taskResourceRep.setDescription(task.getDescription());
taskResourceRep.setStartTime(task.getStartTime());
taskResourceRep.setEndTime(task.getEndTime());
taskResourceRep.setProgress(task.getProgress() != null ? task.getProgress() : 0);
taskResourceRep.setQueuedStartTime(task.getQueuedStartTime());
taskResourceRep.setQueueName(task.getQueueName());
return taskResourceRep;
}
/**
* Creates sysevent record after checking if there are any existing records.
* If force is 1 will not check for existing records.
*/
private synchronized SysEvent createSysEventRecord(URI sysEventId, String opID,
Operation op, int force) {
if (force != 1) {
List sysEvents = dbClient.queryByType(SysEvent.class, true);
if (sysEvents != null && sysEvents.iterator().hasNext()) {
throw APIException.serviceUnavailable.sendEventBusy();
}
}
_log.info("Event id is {} and operation id is {}", sysEventId, opID);
SysEvent sysEvent = new SysEvent();
sysEvent.setId(sysEventId);
sysEvent.setLabel("System Event");
dbClient.createObject(sysEvent);
dbClient.createTaskOpStatus(SysEvent.class, sysEventId, opID, op);
return sysEvent;
}
@Override
public Response sendRegistrationEvent() {
internalSendRegistrationEvent();
return Response.ok().build();
}
void internalSendRegistrationEvent() {
_callHomeEventManager.validateSendEvent();
LicenseInfoListExt licenseList = null;
try {
licenseList = _licenseManager.getLicenseInfoListFromCoordinator();
} catch (Exception e) {
throw APIException.internalServerErrors.licenseInfoNotFoundForType("all license types");
}
if (licenseList != null) {
// send registration events for each registered license type
for (LicenseInfoExt licenseInfo : licenseList.getLicenseList()) {
if (licenseInfo.isTrialLicense()) {
_log.warn("Cannot send regisration event to SYR for trial license {}",
licenseInfo.getLicenseType().toString());
throw APIException.forbidden.permissionDeniedForTrialLicense(
licenseInfo.getLicenseType().toString());
}
_callHomeEventsFacade.sendRegistrationEvent(licenseInfo, getMediaType());
}
}
auditCallhome(OperationTypeEnum.SEND_REGISTRATION,
AuditLogManager.AUDITLOG_SUCCESS, null);
}
@Override
public Response sendHeartbeatEvent() {
// if not configured for callhome, do not continue.
_callHomeEventManager.validateSendEvent();
LicenseInfoListExt licenseList = null;
try {
licenseList = _licenseManager.getLicenseInfoListFromCoordinator();
} catch (Exception e) {
throw APIException.internalServerErrors.licenseInfoNotFoundForType("all license types");
}
if (licenseList != null) {
// send heart beat events for each registered license type
for (LicenseInfoExt licenseInfo : licenseList.getLicenseList()) {
if (licenseInfo.isTrialLicense()) {
_log.warn("Cannot send heartbeat event to SYR for trial license {} ",
licenseInfo.getLicenseType().toString());
throw APIException.forbidden.permissionDeniedForTrialLicense(
licenseInfo.getLicenseType().toString());
}
_callHomeEventsFacade.sendHeartBeatEvent(licenseInfo, getMediaType());
}
}
auditCallhome(OperationTypeEnum.SEND_HEARTBEAT,
AuditLogManager.AUDITLOG_SUCCESS, null);
return Response.ok().build();
}
@Override
public Response getNodeDataForEsrs() {
try {
auditCallhome(OperationTypeEnum.CREATE_ESRS_CONFIGURATION,
AuditLogManager.AUDITLOG_SUCCESS, null);
return Response.status(200).entity(_buildEsrsDevice.build()).build();
} catch (Exception e) {
throw APIException.badRequests.getNodeDataForESRSFailure(e);
}
}
/**
* Spring Injected BuildEsrsConfigurationFile
*/
@Autowired
public void setCallHomeEventsFacade(CallHomeEventsFacade callHomeEventsFacade) {
this._callHomeEventsFacade = callHomeEventsFacade;
}
/**
*
*/
@Autowired
public void setBuildEsrsDevice(BuildEsrsDevice buildEsrsDevice) {
_buildEsrsDevice = buildEsrsDevice;
}
/**
* Record audit log for callhome service
*
* @param auditType Type of AuditLog
* @param operationalStatus Status of operation
* @param description Description for the AuditLog
* @param descparams Description paramters
*/
public void auditCallhome(OperationTypeEnum auditType,
String operationalStatus,
String description,
Object... descparams) {
_auditMgr.recordAuditLog(null, null,
EVENT_SERVICE_TYPE,
auditType,
System.currentTimeMillis(),
operationalStatus,
description,
descparams);
}
/**
* Set the CallHomeEventManager
*/
@Autowired
public void setLicenseManager(LicenseManager licenseManager) {
_licenseManager = licenseManager;
}
/**
* Set the CallHomeEventManager
*/
@Autowired
public void setCallHomeEventManager(CallHomeEventManager callHomeEventManager) {
_callHomeEventManager = callHomeEventManager;
}
}