package org.rhq.enterprise.server.plugins.cloud;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.cloud.Server;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.criteria.ResourceCriteria;
import org.rhq.core.domain.resource.Agent;
import org.rhq.core.domain.resource.Resource;
import org.rhq.enterprise.server.auth.SubjectManagerLocal;
import org.rhq.enterprise.server.cloud.TopologyManagerLocal;
import org.rhq.enterprise.server.operation.OperationManagerLocal;
import org.rhq.enterprise.server.operation.ResourceOperationSchedule;
import org.rhq.enterprise.server.plugin.pc.ControlFacet;
import org.rhq.enterprise.server.plugin.pc.ControlResults;
import org.rhq.enterprise.server.plugin.pc.ScheduledJobInvocationContext;
import org.rhq.enterprise.server.plugin.pc.ServerPluginComponent;
import org.rhq.enterprise.server.plugin.pc.ServerPluginContext;
import org.rhq.enterprise.server.util.LookupUtil;
public class CloudServerPluginComponent implements ServerPluginComponent, ControlFacet {
private static Log log = LogFactory.getLog(CloudServerPluginComponent.class);
public void initialize(ServerPluginContext context) throws Exception {
}
public void start() {
}
public void stop() {
}
public void shutdown() {
}
public ControlResults invoke(String name, Configuration parameters) {
if ("syncServerEndpoint".equals(name)) {
String serverName = parameters.getSimpleValue("name", null);
String serverAddr = parameters.getSimpleValue("address", null);
if (log.isDebugEnabled()) {
log.debug("Invoked syncServerEndpoint with [name: " + serverName + ", address: " + serverAddr + "]");
}
ControlResults results = new ControlResults();
TopologyManagerLocal cloudMgr = LookupUtil.getTopologyManager();
Server server = cloudMgr.getServerByName(serverName);
if (server == null) {
log.warn("Failed to locate server. No address sync will be performed.");
results.setError("No update performed. Failed to find server " + server.getName());
return results;
}
if (serverAddr != null) {
SubjectManagerLocal subjectMgr = LookupUtil.getSubjectManager();
server.setAddress(serverAddr);
cloudMgr.updateServer(subjectMgr.getOverlord(), server);
}
int updateCount = notifyAgents(server);
Configuration complexResults = results.getComplexResults();
complexResults.put(new PropertySimple("results", updateCount + " agents have been updated."));
if (log.isDebugEnabled()) {
log.debug("Notified " + updateCount + " agents of the address change.");
}
return results;
}
return null;
}
public void syncServerEndpoints(ScheduledJobInvocationContext context) {
log.debug("Preparing to sync server endpoints.");
TopologyManagerLocal cloudMgr = LookupUtil.getTopologyManager();
List<Server> servers = cloudMgr.getAllServers();
purgeStaleServers(context, servers);
for (Server server : servers) {
if (!context.containsKey("server:" + server.getName())) {
log.debug("Adding server [" + server.getName() + "] to sync list.");
context.put("server:" + server.getName(), server.getAddress());
} else if (addressChanged(context, server)) {
if (log.isDebugEnabled()) {
log.debug("Detected address change for " + server);
log.debug("Old address was " + context.get("server:" + server.getName() + ", new address is " +
server.getAddress()));
}
context.put("server:" + server.getName(), server.getAddress());
notifyAgents(server);
}
}
}
private void purgeStaleServers(ScheduledJobInvocationContext context, List<Server> servers) {
List<String> purgeList = new ArrayList<String>();
Set<String> serverNames = new HashSet<String>();
for (Server server : servers) {
serverNames.add(server.getName());
}
for (String key : context.getJobData().keySet()) {
if (key.startsWith("server:")) {
String serverName = parseServerName(key);
if (!serverNames.contains(serverName)) {
log.debug("Detected a stale server: " + serverName);
log.debug(serverName + " will be removed from the sync list.");
purgeList.add(key);
}
}
}
for (String staleServer : purgeList) {
context.remove(staleServer);
}
}
private String parseServerName(String key) {
return key.substring("server:".length());
}
private boolean addressChanged(ScheduledJobInvocationContext context, Server server) {
String lastKnownAddr = context.get("server:" + server.getName());
return !server.getAddress().endsWith(lastKnownAddr);
}
@SuppressWarnings("unchecked")
private int notifyAgents(Server server) {
EntityManager entityMgr = LookupUtil.getEntityManager();
String queryString = "select r " +
"from Resource r " +
"where r.resourceType.plugin = :pluginName and " +
"r.resourceType.name = :resourceTypeName and " +
"r.agent in (select a " +
"from Agent a " +
"where a.server = :server)";
List<Resource> agents = entityMgr.createQuery(queryString)
.setParameter("pluginName", "RHQAgent")
.setParameter("resourceTypeName", "RHQ Agent")
.setParameter("server", server)
.getResultList();
if (log.isDebugEnabled()) {
log.debug("Found " + agents.size() + " to be updated with new server endpoint for " + server);
}
int numUpdated = 0;
for (Resource agent : agents) {
updateAgent(agent, server);
numUpdated++;
}
return numUpdated;
}
private void updateAgent(Resource agent, Server server) {
OperationManagerLocal operationMgr = LookupUtil.getOperationManager();
SubjectManagerLocal subjectMgr = LookupUtil.getSubjectManager();
Configuration params = new Configuration();
params.put(new PropertySimple("server", server.getAddress()));
ResourceOperationSchedule schedule = operationMgr.scheduleResourceOperation(subjectMgr.getOverlord(),
agent.getId(), "switchToServer", 0, 0, 0, 0, params, "Cloud Plugin: syncing server endpoint address");
if (log.isDebugEnabled()) {
log.debug("Schedule address sync for agent [name: " + agent.getName() + "].");
log.debug("Operation schedule is " + schedule);
}
}
}