/*
* Copyright (c) 2013 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.systemservices.impl.eventhandler.connectemc;
import java.io.*;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.ws.rs.core.MediaType;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import com.emc.ema.*;
import com.emc.ema.event.objects.EventType;
import com.emc.storageos.coordinator.client.model.PropertyInfoExt;
import com.emc.storageos.coordinator.common.impl.ServiceImpl;
import com.emc.storageos.management.jmx.logging.IdentifierManager;
import com.emc.storageos.services.util.AlertsLogger;
import com.emc.storageos.svcs.errorhandling.resources.APIException;
import com.emc.storageos.svcs.errorhandling.resources.BadRequestException;
import com.emc.storageos.systemservices.exceptions.LocalRepositoryException;
import com.emc.storageos.systemservices.impl.logsvc.LogSvcPropertiesLoader;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.ema.event.objects.ConnectionType;
import com.emc.storageos.model.property.PropertyInfo;
import com.emc.storageos.systemservices.impl.licensing.LicenseInfoExt;
import com.emc.storageos.systemservices.impl.upgrade.CoordinatorClientExt;
import static com.emc.storageos.systemservices.impl.eventhandler.connectemc.CallHomeConstants.*;
public abstract class SendEvent {
private static final Logger _log = LoggerFactory.getLogger(SendEvent.class);
protected String _fileId;
protected IdentifierManager _identifierManager;
private String _networkIpAddress;
protected LicenseInfoExt licenseInfo;
protected MediaType mediaType;
protected LogSvcPropertiesLoader logSvcPropertiesLoader;
protected CoordinatorClientExt coordinator;
private boolean forceAttachLogs = false;
public SendEvent(ServiceImpl service, LogSvcPropertiesLoader logSvcPropertiesLoader,
MediaType mediaType, LicenseInfoExt licenseInfo,
CoordinatorClientExt coordinator) {
URI endpointUri = coordinator.getNodeEndpoint(service.getEndpoint().getHost());
_networkIpAddress = (endpointUri != null) ? coordinator.getIPAddrFromUri(endpointUri)
: service.getEndpoint().getHost();
_log.info("_networkIpAddress: {}", _networkIpAddress);
_identifierManager = IdentifierManager.getInstance();
this.logSvcPropertiesLoader = logSvcPropertiesLoader;
this.mediaType = mediaType;
this.licenseInfo = licenseInfo;
this.coordinator = coordinator;
}
/**
* Call EMC connect service API to generate event files
*/
public void callEMCHome() {
try {
if (licenseInfo == null) {
_log.warn("License information cannot be null. Returning without sending event.");
return;
}
synchronized (SendEvent.class) {
_log.info("callEMCHome(): start ");
_fileId = licenseInfo.getProductId();
// Construct main connect home object that holds the Alert file.
EmaApiConnectHome alertFile = new EmaApiConnectHome();
// Setup the logging information
EmaApiLogType log = new EmaApiLogType();
log.setLogToDirectory(LOG_PATH);
log.setLogToFilename(LOG_FILE_NAME + _fileId + ".log");
// build the common identifier section.
buildIdentifierSection(alertFile);
// build the common identifier section.
buildConnectionSection(alertFile);
// build the event section
buildAlertFile(alertFile, log);
_log.info("callEMCHome(): end ");
}
} catch (APIException api) {
throw api;
} catch (Exception e) {
_log.error("Error occurred while sending event. {}", e);
throw APIException.internalServerErrors.sendEventError(e.getMessage());
}
}
/**
* Builds the Identifier element of ConnectHome
*/
private void buildIdentifierSection(EmaApiConnectHome alertFile) throws
EmaException {
EmaApiIdentifierType identifier = new EmaApiIdentifierType();
EmaApiNode node = alertFile.getNode();
identifier.setClarifyID(licenseInfo.getProductId());
identifier.setDeviceType(_identifierManager.findDeviceType());
identifier.setDeviceState(_identifierManager.findDeviceState());
identifier.setModel(licenseInfo.getModelId());
identifier.setOS(_identifierManager.findOS());
identifier.setOSVER(_identifierManager.findOSVersion());
identifier.setVendor("EMC");
identifier.setSiteName("");
identifier.setSerialNumber(licenseInfo.getProductId());
identifier.setUcodeVer(_identifierManager.findPlatform());
identifier.setWWN(licenseInfo.getLicenseTypeIndicator());
node.setIdentifier(identifier);
overrideIdentifierData(identifier);
}
/**
* Builds the Connection element of ConnectHome
*
* @param alertFile
*/
private void buildConnectionSection(EmaApiConnectHome alertFile) throws EmaException {
ConnectionType connectionType = alertFile.getNode().getConnection();
connectionType.setConnectType(CONNECTION_TYPE_ESRS);
connectionType.setAppName(PRODUCT_NAME);
connectionType.setPort(SECURED_CONNECTION_PORT);
connectionType.setIPAddress(_networkIpAddress);
}
/**
* Get file name from full path
*
* @param fileName
* @return file name
*/
protected String getTargetFileName(String fileName) {
return fileName.substring(fileName.lastIndexOf("/") + 1);
}
/**
* Get attach files' list
*
* @return list of file names
*/
protected abstract ArrayList<String> genAttachFiles() throws Exception;
/**
* Builds alert file with all required information - type of event,
* attachments and sends to ConnectEMC
*/
protected void buildAlertFile(EmaApiConnectHome alertFile,
EmaApiLogType log) throws Exception {
_log.info("Start SendEvent::buildEventType");
alertFile.eventAdd(getEventType(), log);
// Create event file to attach
String eventFilename = CONNECT_EMC_HOME + EmaApiUtils.emaGenerateFilename
(_fileId);
_log.info("Event filename: {}", eventFilename);
ArrayList<String> fileList = genAttachFiles();
BadRequestException badRequestException = null;
if (fileList != null && !fileList.isEmpty()) {
boolean attachLogs = true;
try {
validateAttachmentSize(fileList);
} catch (BadRequestException e) {
if (forceAttachLogs) {
throw e;
}
badRequestException = e;
attachLogs = false;
}
ArrayList<EmaApiFilenameType> attachFiles = new
ArrayList<EmaApiFilenameType>();
if (attachLogs) {
for (String file : fileList) {
EmaApiFilenameType filename = new EmaApiFilenameType();
filename.setQualifiedFileName(file);
filename.setTargetFileName(getTargetFileName(file));
attachFiles.add(filename);
}
}
else { // log size too big, not to attach logs
for (String file : fileList) {
if (file.equals(SYSTEM_LOGS_FILE_PATH) || file.equals
(SYSTEM_EVENT_FILE_PATH)) {
continue;
}
EmaApiFilenameType filename = new EmaApiFilenameType();
filename.setQualifiedFileName(file);
filename.setTargetFileName(getTargetFileName(file));
attachFiles.add(filename);
}
AlertsLogger.getAlertsLogger().warn(
"ConnectEMC alert will be sent without logs attached due to logs have exceeded max allowed size ("
+ this.getAttachmentsMaxSizeMB() + " MB)");
}
alertFile.addFileRawData(eventFilename, attachFiles, log);
}
alertFile.write(eventFilename, log);
alertFile.emaCreateDotEndFile(eventFilename, log);
_log.info("Finish SendEvent::buildEventType");
if (badRequestException != null) {
throw badRequestException;
}
}
/**
* Constructs alert object using EventType class.
*/
protected abstract EventType getEventType() throws Exception;
/**
* Constructs ArrayList of query parameters used
*/
protected ArrayList<String> collectQueryParameters() {
return new ArrayList<String>();
}
/**
* Required to set the appropriate embed level in the identifier section of
* the CallHome event.
*/
protected abstract void overrideIdentifierData(EmaApiIdentifierType identifier)
throws EmaException;
/**
* Generates configuration file with name taken from variable CONFIG_FILE_NAME
* Returns the created file name.
*/
protected String generateConfigFile()
throws JAXBException, LocalRepositoryException, IOException {
ZipOutputStream zos = null;
try {
PropertyInfoExt properties = new PropertyInfoExt(coordinator.getPropertyInfo().getProperties());
zos = new ZipOutputStream(new FileOutputStream
(CONFIG_FILE_PATH));
ZipEntry ze = new ZipEntry(CONFIG_FILE_NAME + getFileExtension());
zos.putNextEntry(ze);
if (MediaType.APPLICATION_JSON_TYPE.equals(mediaType)) {
(new ObjectMapper()).writeValue(zos, properties); // gson should not be used any more
} else {
JAXBContext jaxbContext = JAXBContext.newInstance(PropertyInfo.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(properties, zos);
}
zos.flush();
} finally {
if (zos != null) {
zos.close();
}
}
return CONFIG_FILE_PATH;
}
/**
* Returns file extension based on media type. By default it returns .xml.
*/
protected String getFileExtension() {
_log.debug("Media type requested is {}", mediaType);
if (MediaType.APPLICATION_JSON_TYPE.equals(mediaType)) {
return ".json";
}
return ".xml";
}
/**
* Validates attachment size against maximum allowed of 16MB.
* Throws error if size is more than allowed.
*/
public void validateAttachmentSize(List<String> filePathNames) {
int attachmentsMaxMB = getAttachmentsMaxSizeMB();
if (attachmentsMaxMB == 0 || filePathNames == null ||
filePathNames.isEmpty()) {
return;
}
double attachmentSizeBytes = 0;
double totalLogsSize = 0;
for (String path : filePathNames) {
double size = new File(path).length();
if (size > 0) {
if (path.equals(SYSTEM_LOGS_FILE_PATH) || path.equals
(SYSTEM_EVENT_FILE_PATH)) {
totalLogsSize += size;
}
_log.info("Attachment {} size is {} MB", path, (size / BYTE_TO_MB));
attachmentSizeBytes += size;
}
}
if (attachmentSizeBytes > 0) {
long attachmentSizeMB = (long) Math.ceil(attachmentSizeBytes / BYTE_TO_MB);
if (attachmentSizeMB > attachmentsMaxMB) {
_log.error("Attachments size {} MB is more than allowed max {} MB ",
attachmentSizeMB, attachmentsMaxMB);
if (totalLogsSize > 0) {
totalLogsSize = Math.ceil(totalLogsSize / BYTE_TO_MB);
_log.info("Logs attachment size is {} MB ", totalLogsSize);
throw APIException.badRequests.attachmentLogsSizeError
(attachmentSizeMB,
(long) totalLogsSize, attachmentsMaxMB,
collectQueryParameters().toString());
}
throw APIException.internalServerErrors.attachmentSizeError
(attachmentSizeMB,
attachmentsMaxMB);
}
}
}
/**
* Returns maximum size allowed for attachments.
* Gets the size from properties.
*/
protected int getAttachmentsMaxSizeMB() {
return logSvcPropertiesLoader.getAttachmentMaxSizeMB();
}
public boolean isForceAttachLogs() {
return forceAttachLogs;
}
public void setForceAttachLogs(boolean forceAttachLogs) {
this.forceAttachLogs = forceAttachLogs;
}
}