/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004, 2005, 2006], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program is distributed
* in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.autoinventory.agent.server;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.agent.*;
import org.hyperic.hq.agent.server.*;
import org.hyperic.hq.appdef.shared.AIPlatformValue;
import org.hyperic.hq.appdef.shared.AIServerValue;
import org.hyperic.hq.autoinventory.*;
import org.hyperic.hq.autoinventory.agent.AICommandsAPI;
import org.hyperic.hq.autoinventory.agent.client.AICommandsClient;
import org.hyperic.hq.bizapp.agent.CommandsAPIInfo;
import org.hyperic.hq.bizapp.client.AutoinventoryCallbackClient;
import org.hyperic.hq.bizapp.client.StorageProviderFetcher;
import org.hyperic.hq.common.SystemException;
import org.hyperic.hq.product.AutoinventoryPluginManager;
import org.hyperic.hq.product.ProductPlugin;
import org.hyperic.util.AutoApproveConfig;
import org.hyperic.util.StringUtil;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
public class AutoinventoryCommandsServer implements AgentServerHandler, AgentNotificationHandler, ScanListener {
// max sleep is 1 hour between attempts to send AI report to server.
public static final long AIREPORT_MAX_SLEEP_WAIT = (60000 * 60);
// we'll keep trying for 30 days to send our a report.
public static final long AIREPORT_MAX_TRY_TIME = AIREPORT_MAX_SLEEP_WAIT * 24 * 30;
private AICommandsAPI _verAPI;
private AgentDaemon _agent;
private AgentStorageProvider _storage;
private Log _log;
private RuntimeAutodiscoverer _rtAutodiscoverer;
private AICommandsService _aiCommandsService;
private AutoApproveConfig _autoApproveConfig;
// The CertDN uniquely identifies this agent
protected String _certDN;
private ScanManager _scanManager;
private ScanState _lastCompletedDefaultScanState;
private AutoinventoryCallbackClient _client;
public AutoinventoryCommandsServer(){
_verAPI = new AICommandsAPI();
_log = LogFactory.getLog(AutoinventoryCommandsServer.class);
}
public AgentAPIInfo getAPIInfo(){
return _verAPI;
}
public String[] getCommandSet(){
return AICommandsAPI.commandSet;
}
public AgentRemoteValue dispatchCommand(String cmd, AgentRemoteValue args, InputStream in, OutputStream out)
throws AgentRemoteException {
_log.debug("AICommandsServer: asked to invoke cmd=" + cmd);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
return dispatchCommand_internal(cmd, args);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
public void startup (AgentDaemon agent) throws AgentStartException {
try {
_agent = agent;
_storage = agent.getStorageProvider();
_client = setupClient();
_certDN = _storage.getValue(AgentDaemon.PROP_CERTDN);
} catch(AgentRunningException exc){
throw new AgentAssertionException("Agent should be running here");
}
// Read the auto-approve configuration.
_autoApproveConfig = new AutoApproveConfig(
_agent.getBootConfig().getConfDirName(), AgentConfig.DEFAULT_PROP_ENC_KEY_FILE);
AutoinventoryPluginManager pluginManager;
try {
pluginManager = (AutoinventoryPluginManager)
agent.getPluginManager(ProductPlugin.TYPE_AUTOINVENTORY);
} catch (Exception e) {
throw new AgentStartException("Unable to get auto inventory " +
"plugin manager: " +
e.getMessage());
}
// Initialize the runtime autodiscoverer
_rtAutodiscoverer = new RuntimeAutodiscoverer(this, _storage, _agent, _client);
// Fire up the scan manager
_scanManager = new ScanManager(this, pluginManager, _rtAutodiscoverer, _autoApproveConfig);
_aiCommandsService = new AICommandsService(pluginManager, _rtAutodiscoverer, _scanManager);
AgentTransportLifecycle agentTransportLifecycle;
try {
agentTransportLifecycle = agent.getAgentTransportLifecycle();
} catch (Exception e) {
throw new AgentStartException("Unable to get agent transport lifecycle: "+
e.getMessage());
}
_log.info("Registering AI Commands Service with Agent Transport");
try {
agentTransportLifecycle.registerService(AICommandsClient.class, _aiCommandsService);
} catch (Exception e) {
throw new AgentStartException("Failed to register AI Commands Service.", e);
}
_scanManager.startup();
// Do we have a provider?
if ( CommandsAPIInfo.getProvider(_storage) == null ) {
agent.registerNotifyHandler(this,
CommandsAPIInfo.NOTIFY_SERVER_SET);
} else {
_rtAutodiscoverer.triggerDefaultScan();
}
_log.info("Autoinventory Commands Server started up");
}
public void handleNotification(String msgClass, String msg) {
if (msgClass.equals(CommandsAPIInfo.NOTIFY_SERVER_SET)) {
_scanManager.interruptHangingScan();
_rtAutodiscoverer.triggerDefaultScan();
}
}
public final void postInitActions() throws AgentStartException { /*do nothing*/ }//EOM
public void shutdown () {
_log.info("Autoinventory Commands Server shutting down");
// Give the scan manager 3 seconds to shut down.
synchronized ( _scanManager ) {
_scanManager.shutdown(3000);
}
_log.info("Autoinventory Commands Server shut down");
}
/**
* This is where we report our autoinventory-detected data to
* the EAM server.
* @see org.hyperic.hq.autoinventory.ScanListener#scanComplete
*/
public void scanComplete (ScanState scanState) throws AutoinventoryException, SystemException {
// Special handling for periodic default scans
if (scanState.getIsDefaultScan()) {
if (_lastCompletedDefaultScanState != null) {
try {
if (_lastCompletedDefaultScanState.isSameState(scanState)) {
// If this default scan is the same as the last one,
// don't send anything to the server
_log.debug("Default scan didn't find any changes, not "
+ "sending report to the server");
return;
}
} catch (AutoinventoryException e) {
// Just log it and continue, I guess we'll send the report
// to the server in this case
_log.error("Error comparing default scan states: " + e, e);
}
}
_lastCompletedDefaultScanState = scanState;
}
// Anytime a scan completes, we update the most recent state
_aiCommandsService.setMostRecentState(scanState);
// Issue a warning if we could not even detect the platform
AIPlatformValue aiPlatformValue = scanState.getPlatform();
if (aiPlatformValue == null) {
try {
ByteArrayOutputStream errInfo = new ByteArrayOutputStream();
PrintStream errInfoPS = new PrintStream(errInfo);
scanState.printFullStatus(errInfoPS);
_log.warn("AICommandsServer: scan completed, but we could not even "
+ "detect the platform, so nothing will be reported "
+ "to the server. Here is some information about the error "
+ "that occurred: \n" + errInfo.toString() + "\n");
} catch ( Exception e ) {
_log.warn("AICommandsServer: scan completed, but we could not even "
+ "detect the platform, so nothing will be reported "
+ "to the server. More information would be provided, "
+ "but this error occurred just trying to generate more "
+ "information about the error: " + e, e);
}
}
// Handle auto approval
applyAutoApproval(aiPlatformValue);
// But regardless, we always report back to the server, so it
// knows the scan has been completed.
scanState.setCertDN(_certDN);
long sleepWaitMillis = 15000;
long firstTryTime = System.currentTimeMillis();
long diffTime;
while ( true ) {
try {
if (_log.isDebugEnabled()) {
_log.debug("Sending autoinventory report to server: "
+ scanState
/*+ "\nWITH SERVERS=" + StringUtil.iteratorToString(scanState.getAllServers(null).iterator())*/);
}
_client.aiSendReport(scanState);
_log.info("Autoinventory report " +
"successfully sent to server.");
break;
} catch (Exception e) {
diffTime = System.currentTimeMillis() - firstTryTime;
if (diffTime > AIREPORT_MAX_TRY_TIME) {
final String eMsg = "Unable to send autoinventory " +
"platform data to server for maximum time of " +
StringUtil.formatDuration(AIREPORT_MAX_TRY_TIME) +
", giving up. Error was: " + e.getMessage();
if(_log.isDebugEnabled()){
_log.debug(eMsg, e);
} else {
_log.error(eMsg);
}
return;
}
final String eMsg = "Unable to send autoinventory " +
"platform data to server, sleeping for " +
String.valueOf(sleepWaitMillis/1000) + " secs before "+
"retrying. Error: " + e.getMessage();
if(_log.isDebugEnabled()){
_log.debug(eMsg, e);
} else {
_log.error(eMsg);
}
try {
Thread.sleep(sleepWaitMillis);
sleepWaitMillis += (sleepWaitMillis/2);
if ( sleepWaitMillis > AIREPORT_MAX_SLEEP_WAIT ) {
sleepWaitMillis = AIREPORT_MAX_SLEEP_WAIT;
}
} catch ( InterruptedException ie ) { /* ignore */ }
}
}
}
/**
* This is the scan that's run when the agent first starts up,
* and periodically thereafter. This method is called by the
* ScanManager when the RuntimeAutodiscoverer says it's time for
* a DefaultScan (by default, every 15 mins)
*/
protected void scheduleDefaultScan () {
_log.debug("Scheduling DefaultScan...");
ScanConfiguration scanConfig = new ScanConfiguration();
scanConfig.setIsDefaultScan(true);
_aiCommandsService.startScan(scanConfig);
}
private AgentRemoteValue dispatchCommand_internal(String cmd, AgentRemoteValue args)
throws AgentRemoteException {
// Anytime we get a request from the server, it means the server
// is available. So, if there is a scan sleeping in "scanComplete",
// wake it up now
_scanManager.interruptHangingScan();
if(cmd.equals(AICommandsAPI.command_startScan)){
ScanConfigurationCore scanConfig;
try {
scanConfig = ScanConfigurationCore.fromAgentRemoteValue(AICommandsAPI.PROP_SCANCONFIG, args);
} catch ( Exception e ) {
_log.error("Error starting scan.", e);
throw new AgentRemoteException("Error starting scan: " +
e.toString());
}
_aiCommandsService.startScan(scanConfig, false);
return null;
} else if(cmd.equals(AICommandsAPI.command_stopScan)){
_aiCommandsService.stopScan(false);
return null;
} else if(cmd.equals(AICommandsAPI.command_getScanStatus)){
AgentRemoteValue rval = new AgentRemoteValue();
ScanStateCore state = _aiCommandsService.getScanStatus(false);
try {
state.toAgentRemoteValue("scanState", rval);
} catch ( Exception e ) {
_log.error("Error getting scan state.", e);
throw new AgentRemoteException("Error getting scan status: " +
e.toString());
}
return rval;
} else if(cmd.equals(AICommandsAPI.command_pushRuntimeDiscoveryConfig)){
_aiCommandsService.pushRuntimeDiscoveryConfig(args, false);
return null;
} else {
throw new AgentRemoteException("Unknown command: " + cmd);
}
}
private AutoinventoryCallbackClient setupClient() {
StorageProviderFetcher fetcher =
new StorageProviderFetcher(_storage);
return new AutoinventoryCallbackClient(fetcher);
}
private void applyAutoApproval(AIPlatformValue aiPlatformValue) {
// If the auto-approve configuration wasn't provided then exit.
if (!_autoApproveConfig.exists()) {
_log.info("no resource is auto approved");
return;
}
// Platform auto-approval
boolean approvePlatform = _autoApproveConfig.isAutoApproved(AutoApproveConfig.PLATFORM_PROPERTY_NAME);
aiPlatformValue.setAutoApprove(approvePlatform);
// Servers auto-approval
AIServerValue[] aiServerValues = aiPlatformValue.getAIServerValues();
if (aiServerValues != null) {
for (AIServerValue aiServerValue : aiServerValues) {
boolean approveServer = _autoApproveConfig.isAutoApproved(aiServerValue.getName());
_log.info("--- Auto-Approve for Server: [" + aiServerValue.getName() + "] is: " + approveServer);
aiServerValue.setAutoApprove(approveServer);
}
}
}
}