/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.enterprise.server.cloud;
import java.util.List;
import java.util.Map;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Permission;
import org.rhq.core.domain.cloud.FailoverList;
import org.rhq.core.domain.cloud.PartitionEvent;
import org.rhq.core.domain.cloud.PartitionEvent.ExecutionStatus;
import org.rhq.core.domain.cloud.PartitionEventDetails;
import org.rhq.core.domain.cloud.PartitionEventType;
import org.rhq.core.domain.cloud.composite.FailoverListComposite;
import org.rhq.core.domain.criteria.PartitionEventCriteria;
import org.rhq.core.domain.resource.Agent;
import org.rhq.core.domain.server.PersistenceUtility;
import org.rhq.core.domain.util.PageControl;
import org.rhq.core.domain.util.PageList;
import org.rhq.core.domain.util.PageOrdering;
import org.rhq.enterprise.server.RHQConstants;
import org.rhq.enterprise.server.authz.RequiredPermission;
import org.rhq.enterprise.server.authz.RequiredPermissions;
import org.rhq.enterprise.server.core.AgentManagerLocal;
import org.rhq.enterprise.server.purge.PurgeManagerLocal;
import org.rhq.enterprise.server.util.CriteriaQueryGenerator;
import org.rhq.enterprise.server.util.CriteriaQueryRunner;
import org.rhq.enterprise.server.util.QueryUtility;
/**
* This session beans acts as the underlying implementation the distribution algorithm will
* interact. The distribution algorithm runs as a result of various changes in the system
* including but not limited to: newly registering agents, currently connecting agents, cloud
* membership changes (server added/removed), and redistributions according to agent load. Each
* of these changes is captured as a {@link PartitionEvent}, and the distribution will either
* need to generated a single (or a set of) {@link FailoverList} objects that are sent down to
* the connected agents. The agents then use these lists to determine which server to fail over
* to, if their primary server is unreachable and/or goes down.
*
* @author Joseph Marques
* @author Jay Shaughnessy
*/
@Stateless
public class PartitionEventManagerBean implements PartitionEventManagerLocal {
private final Log LOG = LogFactory.getLog(PartitionEventManagerBean.class);
@PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME)
private EntityManager entityManager;
@EJB
AgentManagerLocal agentManager;
@EJB
FailoverListManagerLocal failoverListManager;
@EJB
PurgeManagerLocal purgeManager;
@EJB
//@IgnoreDependency
PartitionEventManagerLocal partitionEventManager;
@RequiredPermission(Permission.MANAGE_INVENTORY)
public FailoverListComposite agentPartitionEvent(Subject subject, String agentName, PartitionEventType eventType,
String eventDetail) {
if (eventType.isCloudPartitionEvent() || (null == agentName)) {
throw new IllegalArgumentException("Invalid agent partition event or no agent specified for event type: "
+ eventType);
}
Agent agent = agentManager.getAgentByName(agentName);
if (null == agent) {
throw new IllegalArgumentException("Can not perform partition event, agent not found with name: "
+ agentName);
}
PartitionEvent partitionEvent = new PartitionEvent(subject.getName(), eventType, eventDetail,
PartitionEvent.ExecutionStatus.IMMEDIATE);
partitionEventManager.createPartitionEvent(subject, partitionEvent);
return failoverListManager.getForSingleAgent(partitionEvent, agent.getName());
}
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
@RequiredPermission(Permission.MANAGE_INVENTORY)
public void createPartitionEvent(Subject subject, PartitionEvent partitionEvent) {
entityManager.persist(partitionEvent);
}
@RequiredPermission(Permission.MANAGE_INVENTORY)
public Map<Agent, FailoverListComposite> cloudPartitionEvent(Subject subject, PartitionEventType eventType,
String eventDetail) {
if (!eventType.isCloudPartitionEvent()) {
throw new IllegalArgumentException("Invalid cloud partition event type: " + eventType);
}
PartitionEvent partitionEvent = new PartitionEvent(subject.getName(), eventType, eventDetail,
PartitionEvent.ExecutionStatus.IMMEDIATE);
entityManager.persist(partitionEvent);
return failoverListManager.refresh(partitionEvent);
}
@RequiredPermissions({ @RequiredPermission(Permission.MANAGE_SETTINGS),
@RequiredPermission(Permission.MANAGE_INVENTORY) })
public void cloudPartitionEventRequest(Subject subject, PartitionEventType eventType, String eventDetail) {
if (!eventType.isCloudPartitionEvent()) {
throw new IllegalArgumentException("Invalid cloud partition event type: " + eventType);
}
PartitionEvent partitionEvent = new PartitionEvent(subject.getName(), eventType, eventDetail,
PartitionEvent.ExecutionStatus.REQUESTED);
entityManager.persist(partitionEvent);
}
@RequiredPermission(Permission.MANAGE_INVENTORY)
public void auditPartitionEvent(Subject subject, PartitionEventType eventType, String eventDetail) {
PartitionEvent partitionEvent = new PartitionEvent(subject.getName(), eventType, eventDetail,
PartitionEvent.ExecutionStatus.AUDIT);
entityManager.persist(partitionEvent);
}
@RequiredPermissions({ @RequiredPermission(Permission.MANAGE_SETTINGS),
@RequiredPermission(Permission.MANAGE_INVENTORY) })
public void deletePartitionEvents(Subject subject, Integer[] partitionEventIds) {
for (int partitionEventId : partitionEventIds) {
PartitionEvent doomedEvent = entityManager.find(PartitionEvent.class, partitionEventId);
entityManager.remove(doomedEvent); // cascade rules should take care of this
}
}
@RequiredPermissions({ @RequiredPermission(Permission.MANAGE_SETTINGS),
@RequiredPermission(Permission.MANAGE_INVENTORY) })
public int purgeAllEvents(Subject subject) {
long deleteUpToTime = System.currentTimeMillis();
return purgeManager.purgePartitionEvents(deleteUpToTime);
}
public void processRequestedPartitionEvents() {
boolean completedRequest = false;
Query query = entityManager.createNamedQuery(PartitionEvent.QUERY_FIND_BY_EXECUTION_STATUS);
query.setParameter("executionStatus", PartitionEvent.ExecutionStatus.REQUESTED);
@SuppressWarnings("unchecked")
List<PartitionEvent> requestedPartitionEvents = query.getResultList();
for (PartitionEvent next : requestedPartitionEvents) {
// in the rare case of multiple requested partitioning events, just perform one and set
// the rest completed. There is no sense in repartitioning multiple times on the same data.
if (!completedRequest) {
if (!next.getEventType().isCloudPartitionEvent()) {
LOG.warn("Invalid cloud partition event type: " + next.getEventType());
}
try {
failoverListManager.refresh(next);
completedRequest = true;
} catch (Exception e) {
LOG.warn("Failed requested partition event. Setting COMPLETED to avoid repeated failure: " + e);
}
}
next.setExecutionStatus(PartitionEvent.ExecutionStatus.COMPLETED);
}
}
@RequiredPermission(Permission.MANAGE_INVENTORY)
public PartitionEvent getPartitionEvent(Subject subject, int partitionEventId) {
PartitionEvent event = entityManager.find(PartitionEvent.class, partitionEventId);
return event;
}
@SuppressWarnings("unchecked")
@RequiredPermission(Permission.MANAGE_INVENTORY)
public PageList<PartitionEvent> getPartitionEvents(Subject subject, PartitionEventType type,
ExecutionStatus status, String details, PageControl pageControl) {
pageControl.initDefaultOrderingField("pe.ctime", PageOrdering.DESC);
Query query = PersistenceUtility.createQueryWithOrderBy(entityManager, PartitionEvent.QUERY_FIND_ALL,
pageControl);
Query countQuery = PersistenceUtility.createCountQuery(entityManager, PartitionEvent.QUERY_FIND_ALL);
details = QueryUtility.formatSearchParameter(details);
query.setParameter("type", type);
countQuery.setParameter("type", type);
query.setParameter("status", status);
countQuery.setParameter("status", status);
query.setParameter("details", details);
countQuery.setParameter("details", details);
query.setParameter("escapeChar", QueryUtility.getEscapeCharacter());
countQuery.setParameter("escapeChar", QueryUtility.getEscapeCharacter());
List<PartitionEvent> results = query.getResultList();
long count = (Long) countQuery.getSingleResult();
return new PageList<PartitionEvent>(results, (int) count, pageControl);
}
@SuppressWarnings("unchecked")
@RequiredPermissions({ @RequiredPermission(Permission.MANAGE_SETTINGS),
@RequiredPermission(Permission.MANAGE_INVENTORY) })
public PageList<PartitionEventDetails> getPartitionEventDetails(Subject subject, int partitionEventId,
PageControl pageControl) {
pageControl.initDefaultOrderingField("ped.id", PageOrdering.ASC);
Query query = PersistenceUtility.createQueryWithOrderBy(entityManager,
PartitionEventDetails.QUERY_FIND_BY_EVENT_ID, pageControl);
Query countQuery = PersistenceUtility.createCountQuery(entityManager,
PartitionEventDetails.QUERY_FIND_BY_EVENT_ID);
query.setParameter("eventId", partitionEventId);
countQuery.setParameter("eventId", partitionEventId);
List<PartitionEventDetails> detailsList = query.getResultList();
long count = (Long) countQuery.getSingleResult();
return new PageList<PartitionEventDetails>(detailsList, (int) count, pageControl);
}
@RequiredPermissions({ @RequiredPermission(Permission.MANAGE_SETTINGS),
@RequiredPermission(Permission.MANAGE_INVENTORY) })
public PageList<PartitionEvent> findPartitionEventsByCriteria(Subject subject, PartitionEventCriteria criteria) {
CriteriaQueryGenerator generator = new CriteriaQueryGenerator(subject, criteria);
CriteriaQueryRunner<PartitionEvent> runner = new CriteriaQueryRunner<PartitionEvent>(criteria, generator,
entityManager);
return runner.execute();
}
}