/** * 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) [2013], VMware, 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.topn; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; import javax.annotation.PostConstruct; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.hq.agent.AgentConnectionException; import org.hyperic.hq.agent.AgentRemoteException; import org.hyperic.hq.appdef.server.session.Platform; import org.hyperic.hq.appdef.server.session.ResourceCreatedZevent; import org.hyperic.hq.appdef.server.session.ResourceDeletedZevent; import org.hyperic.hq.appdef.server.session.ResourceRefreshZevent; import org.hyperic.hq.appdef.server.session.ResourceUpdatedZevent; import org.hyperic.hq.appdef.server.session.ResourceZevent; import org.hyperic.hq.appdef.shared.AppdefEntityID; import org.hyperic.hq.appdef.shared.ConfigManager; import org.hyperic.hq.appdef.shared.PlatformManager; import org.hyperic.hq.authz.shared.AuthzSubjectManager; import org.hyperic.hq.common.ApplicationException; import org.hyperic.hq.common.shared.ServerConfigManager; import org.hyperic.hq.hibernate.SessionManager; import org.hyperic.hq.hibernate.SessionManager.SessionRunner; import org.hyperic.hq.measurement.agent.client.MeasurementCommandsClient; import org.hyperic.hq.measurement.agent.client.MeasurementCommandsClientFactory; import org.hyperic.hq.measurement.agent.commands.ScheduleTopn_args; import org.hyperic.hq.measurement.data.TopNConfigurationProperties; import org.hyperic.hq.measurement.server.session.TopNSchedule; import org.hyperic.hq.measurement.server.session.TopNScheduleDAO; import org.hyperic.hq.measurement.shared.TopNManager; import org.hyperic.hq.product.ProductPlugin; import org.hyperic.hq.zevents.Zevent; import org.hyperic.hq.zevents.ZeventEnqueuer; import org.hyperic.hq.zevents.ZeventListener; import org.hyperic.util.ConfigPropertyException; import org.hyperic.util.config.ConfigResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.xerial.snappy.Snappy; @Component public class TopNManagerImpl implements ZeventListener<ResourceZevent>, TopNManager { public final static String TOPN_DEFAULT_INTERVAL = "TOPN_DEFAULT_INTERVAL"; public final static String TOPN_NUMBER_OF_PROCESSES = "TOPN_NUMBER_OF_PROCESSES"; private final Log log = LogFactory.getLog(TopNManagerImpl.class); private final ZeventEnqueuer zEventManager; private final PlatformManager platformManager; private final MeasurementCommandsClientFactory measurementCommandsClientFactory; private final ServerConfigManager serverConfigManager; private final AuthzSubjectManager authzSubjectManager; private final ConfigManager configManager; private final TopNScheduleDAO topNScheduleDao; @Autowired public TopNManagerImpl(ZeventEnqueuer zEventManager, PlatformManager platformManager, MeasurementCommandsClientFactory measurementCommandsClientFactory, ServerConfigManager serverConfigManager, AuthzSubjectManager authzSubjectManager, ConfigManager configManager, TopNScheduleDAO topNScheduleDao) { this.zEventManager = zEventManager; this.platformManager = platformManager; this.measurementCommandsClientFactory = measurementCommandsClientFactory; this.serverConfigManager = serverConfigManager; this.authzSubjectManager = authzSubjectManager; this.configManager = configManager; this.topNScheduleDao = topNScheduleDao; } @PostConstruct public void subscribe() { Set<Class<? extends Zevent>> listenEvents = new HashSet<Class<? extends Zevent>>(); listenEvents.add(ResourceCreatedZevent.class); listenEvents.add(ResourceUpdatedZevent.class); listenEvents.add(ResourceRefreshZevent.class); listenEvents.add(ResourceDeletedZevent.class); zEventManager.addBufferedListener(listenEvents, this); } @Transactional public void scheduleTopNCollection(AppdefEntityID id, ConfigResponse config) { log.info("Rescheduling TopN collection for platform '" + id.getId() + "', since scheduling parameters has changed"); Platform platform = platformManager.getPlatformById(id.getId()); if(!isTopNSupported(platform)) { return; } if(!updateScheduleObject(config, platform.getResource().getId())){ return; } if (config.getValue(TopNConfigurationProperties.ENABLE_TOPN_COLLECTION.getName()) == null || !config.getValue(TopNConfigurationProperties.ENABLE_TOPN_COLLECTION.getName()).equalsIgnoreCase("true")){ unscheduleTopNCollection(platform.getResource().getId(), config); return; } scheduleTopNCollection(platform, config); } public int getNumberOfProcessesToShowForPlatform(int resourceId) { Platform platform = platformManager.getPlatformByResourceId(resourceId); ConfigResponse config; try { config = configManager.getMergedConfigResponse(authzSubjectManager.getOverlordPojo(), ProductPlugin.TYPE_MEASUREMENT, platform.getEntityId(), true); } catch (Exception e) { return -1; } return Integer.valueOf(config.getValue(TopNConfigurationProperties.TOPN_NUMBER_OF_PROCESSES.getName())); } @Transactional public void scheduleTopNCollection(int resourceId, int intervalInMinutes, int numberOfProcesses) { ConfigResponse config; Platform platform = platformManager.getPlatformByResourceId(resourceId); if(!isTopNSupported(platform)) { return; } try { config = configManager.getMergedConfigResponse(authzSubjectManager.getOverlordPojo(), ProductPlugin.TYPE_MEASUREMENT, platform.getEntityId(), true); } catch (Exception e) { return; } configureTopNSchedule(platform, config, intervalInMinutes, numberOfProcesses); scheduleTopNCollection(platform, config); } @Transactional public void updateGlobalTopNInterval(int intervalInMinutes) { log.info("Updating Top Processes interval in minutes for all platforms to '" + intervalInMinutes + "'"); try { Properties newProps = serverConfigManager.getConfig(); newProps.put(TOPN_DEFAULT_INTERVAL, String.valueOf(intervalInMinutes)); serverConfigManager.setConfig(authzSubjectManager.getOverlordPojo(), newProps); } catch (ApplicationException e) { log.error("Error updating TopN interval", e); return; } catch (ConfigPropertyException e) { log.error("Error updating TopN interval", e); return; } for (TopNSchedule schedule : topNScheduleDao.findAll()) { scheduleTopNCollection(schedule.getResourceId(), intervalInMinutes, schedule.getNumberOfProcesses()); } } @Transactional public void updateGlobalTopNNumberOfProcesses(int numberOfProcesses) { log.info("Updating number of processes to collect for all platforms to '" + numberOfProcesses + "'"); try { Properties newProps = serverConfigManager.getConfig(); newProps.put(TOPN_NUMBER_OF_PROCESSES, String.valueOf(numberOfProcesses)); serverConfigManager.setConfig(authzSubjectManager.getOverlordPojo(), newProps); } catch (ApplicationException e) { log.error("Error updating TopN interval", e); return; } catch (ConfigPropertyException e) { log.error("Error updating TopN interval", e); return; } for (TopNSchedule schedule : topNScheduleDao.findAll()) { scheduleTopNCollection(schedule.getResourceId(), schedule.getIntervalInMinutes(), numberOfProcesses); } } @Transactional public void unscheduleGlobalTopNCollection() { log.info("Disabling Top Processes collection for all platforms"); try { Properties newProps = serverConfigManager.getConfig(); newProps.put(TOPN_DEFAULT_INTERVAL, "0"); serverConfigManager.setConfig(authzSubjectManager.getOverlordPojo(), newProps); } catch (ApplicationException e) { log.error("Error unscheduling TopN collection", e); return; } catch (ConfigPropertyException e) { log.error("Error unscheduling TopN collection", e); return; } for (TopNSchedule schedule : topNScheduleDao.findAll()) { if (schedule.isEnabled()) { unscheduleTopNCollection(schedule.getResourceId()); } } } @Transactional public void unscheduleTopNCollection(int resourceId) { unscheduleTopNCollection(resourceId, null); } @Transactional public void unscheduleTopNCollection(int resourceId, ConfigResponse config) { log.info("Unscheduling Top Processes collection for resource '" + resourceId + "'"); Platform platform = platformManager.getPlatformByResourceId(resourceId); if (null == config) { try { config = configManager.getMergedConfigResponse(authzSubjectManager.getOverlordPojo(), ProductPlugin.TYPE_MEASUREMENT, platform.getEntityId(), true); } catch (Exception e) { return; } } MeasurementCommandsClient client = measurementCommandsClientFactory.getClient(platform.getAgent()); // Update ConfigResponse config.setValue(TopNConfigurationProperties.ENABLE_TOPN_COLLECTION.getName(), false); persistConfigResponse(platform, config); // Update the TopNSchedule object TopNSchedule scheduleData = topNScheduleDao.get(resourceId); scheduleData.setEnabled(false); scheduleData.setLastUpdated(System.currentTimeMillis()); topNScheduleDao.save(scheduleData); // Send the unschedule command to the client try { client.unscheduleTopn(); } catch (AgentRemoteException ex) { log.error("Error while unscheduling TopN", ex); } catch (AgentConnectionException ex) { log.error("Error while unscheduling TopN", ex); } } private void persistConfigResponse(Platform platform, ConfigResponse config) { try { configManager.setConfigResponse(authzSubjectManager.getOverlordPojo(), platform.getEntityId(), config, ProductPlugin.TYPE_MEASUREMENT, false, false); } catch (Exception e) { } } public void processEvents(List<ResourceZevent> list) { for (final ResourceZevent e : list) { final AppdefEntityID id = e.getAppdefEntityID(); if (!id.isPlatform()) { return; } try { SessionManager.runInSession(new SessionRunner() { public void run() throws Exception { if (e instanceof ResourceDeletedZevent) { if(null != e.getResourceId()) { TopNSchedule schedule = topNScheduleDao.get(e.getResourceId()); if (null != schedule) { if (log.isDebugEnabled()) { log.debug("Deleting TopNSchedule entry with id - '" + e.getResourceId() + "'"); } topNScheduleDao.remove(schedule); topNScheduleDao.getSession().flush(); } } return; } Platform platform = platformManager.getPlatformById(id.getId()); if(!isTopNSupported(platform)) { return; } if (Integer.valueOf(serverConfigManager.getPropertyValue(TOPN_DEFAULT_INTERVAL)) <= 0) { if (log.isDebugEnabled()) { log.debug("TopN collection is disabled, not scheduling for platform '" + platform.getFqdn() + "'"); } return; } ConfigResponse config = configManager.getMergedConfigResponse( authzSubjectManager.getOverlordPojo(), ProductPlugin.TYPE_MEASUREMENT, id, true); if (e instanceof ResourceCreatedZevent) { //Remove creation due to bug HQ-4786, we will create the object in ResourceUpdatedZevent return; } else if (e instanceof ResourceUpdatedZevent) { TopNSchedule schedule = topNScheduleDao.get(platform.getResource().getId()); if (null == schedule) { configureDefaultTopNSchedule(platform, config); log.info("Scheduling TopN collection for platform '" + platform.getResource().getId() + "'"); scheduleTopNCollection(platform, config); } else { int interval = Integer.valueOf(config .getValue(TopNConfigurationProperties.TOPN_COLLECTION_INTERVAL_IN_MINUTES .getName())); int numberOfProcesses = Integer.valueOf(config .getValue(TopNConfigurationProperties.TOPN_NUMBER_OF_PROCESSES.getName())); boolean enabled = Boolean.valueOf(config .getValue(TopNConfigurationProperties.ENABLE_TOPN_COLLECTION.getName())); if ((interval != schedule.getIntervalInMinutes()) || (numberOfProcesses != schedule.getNumberOfProcesses()) || (enabled != schedule.isEnabled())) { topNScheduleDao.getSession().clear(); scheduleTopNCollection(id, config); } } } else if (e instanceof ResourceRefreshZevent) { TopNSchedule schedule = topNScheduleDao.get(platform.getResource().getId()); if ((null != schedule) && !schedule.isEnabled()) { return; } log.info("Rescheduling TopN collection for platform '" + platform.getResource().getId() + "'"); if (null == schedule) { configureDefaultTopNSchedule(platform, config); } scheduleTopNCollection(platform, config); } } public String getName() { return "TopNEventsListener"; } }); } catch (Exception ex) { log.error(ex, ex); return; } } } private void configureDefaultTopNSchedule(Platform platform, ConfigResponse config) { if (log.isDebugEnabled()) { log.debug("Configuring ConfigResponse with default Top Processes parameters for platform '" + platform.getName() + "'"); } configureTopNSchedule(platform, config, Integer.valueOf(serverConfigManager.getPropertyValue(TOPN_DEFAULT_INTERVAL)), Integer.valueOf(serverConfigManager.getPropertyValue(TOPN_NUMBER_OF_PROCESSES))); } private boolean isTopNSupported(Platform platform){ if (null == platform) { return false; } if (!agentVersionValid(platform)) { return false; } if (!platform.getPlatformType().getPlugin().equalsIgnoreCase("system")) { return false; } return true; } private boolean agentVersionValid(Platform platform) { // TODO:Revisit this // TopN is supported only for agents with version >= // 5.8.0 if (0 > platform.getAgent().getVersion().compareTo("5.8.0")) { if (log.isDebugEnabled()) { log.debug("The agent running on platform '" + platform.getName() + "' version is older than 5.8, not schedulig Top Processes collection"); } return false; } return true; } private void scheduleTopNCollection(Platform platform, ConfigResponse config) { if (!config.getValue(TopNConfigurationProperties.ENABLE_TOPN_COLLECTION.getName()).equalsIgnoreCase("true")) { return; } int interval = Integer.valueOf(config.getValue(TopNConfigurationProperties.TOPN_COLLECTION_INTERVAL_IN_MINUTES .getName())); int numberOfProcesses = Integer.valueOf(config.getValue(TopNConfigurationProperties.TOPN_NUMBER_OF_PROCESSES .getName())); MeasurementCommandsClient client = measurementCommandsClientFactory.getClient(platform.getAgent()); try { client.scheduleTopn(new ScheduleTopn_args(interval, null, numberOfProcesses)); } catch (AgentRemoteException ex) { log.error("Error while scheduling TopN", ex); } catch (AgentConnectionException ex) { log.error("Error while scheduling TopN", ex); } } private void configureTopNSchedule(Platform platform, ConfigResponse config, int interval, int numberOfProcesses) { config.setValue(TopNConfigurationProperties.ENABLE_TOPN_COLLECTION.getName(), true); config.setValue(TopNConfigurationProperties.TOPN_COLLECTION_INTERVAL_IN_MINUTES.getName(), interval); config.setValue(TopNConfigurationProperties.TOPN_NUMBER_OF_PROCESSES.getName(), numberOfProcesses); persistConfigResponse(platform, config); updateScheduleObject(platform.getResource().getId(), true, interval, numberOfProcesses); } private boolean updateScheduleObject(ConfigResponse config, int resourceId) { //Add default values String intervalValue = config.getValue(TopNConfigurationProperties.TOPN_COLLECTION_INTERVAL_IN_MINUTES.getName()); String numberOfProcesseslValue = config.getValue(TopNConfigurationProperties.TOPN_NUMBER_OF_PROCESSES.getName()); String enabledValue = config.getValue(TopNConfigurationProperties.ENABLE_TOPN_COLLECTION.getName()); //In case of null values, dont crate schedule and return if (intervalValue == null || numberOfProcesseslValue == null || enabledValue == null){ return false; } int interval = Integer.valueOf(intervalValue); int numberOfProcesses = Integer.valueOf(numberOfProcesseslValue); boolean enabled = Boolean.valueOf(enabledValue); updateScheduleObject(resourceId, enabled, interval, numberOfProcesses); return true; } private void updateScheduleObject(int resourceId, boolean enabled, int interval, int numberOfProcesses) { if (log.isDebugEnabled()) { log.debug("Updating 'TopNSchedule' object with the following attributes - resourceId='" + resourceId + ", enabled='" + enabled + "', interval='" + interval + "', numberOfProcesses='" + numberOfProcesses + "'"); } TopNSchedule schedule = topNScheduleDao.get(resourceId) ; if(schedule == null){ schedule = new TopNSchedule(); } schedule.setResourceId(resourceId); schedule.setLastUpdated(System.currentTimeMillis()); schedule.setIntervalInMinutes(interval); schedule.setNumberOfProcesses(numberOfProcesses); schedule.setEnabled(enabled); topNScheduleDao.save(schedule); } public byte[] compressData(final byte[] data) { try { return Snappy.compress(data); } catch (final IOException e) { log.error("Unable to compress TopN data " + e.getClass().getSimpleName(), e); return data; } } public byte[] uncompressData(final byte[] data) { try { return Snappy.uncompress(data); } catch (final IOException e) { log.error("Unable to uncompress TopN data " + e.getClass().getSimpleName(), e); return data; } } }