/*
* RHQ Management Platform
* Copyright (C) 2005-2014 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.enterprise.server.resource.metadata.test;
import java.io.FileNotFoundException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.persistence.NoResultException;
import javax.transaction.Status;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.util.ValidationEventCollector;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.rhq.core.clientapi.agent.measurement.MeasurementAgentService;
import org.rhq.core.clientapi.descriptor.DescriptorPackages;
import org.rhq.core.clientapi.descriptor.plugin.PluginDescriptor;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.cloud.Server;
import org.rhq.core.domain.criteria.ResourceCriteria;
import org.rhq.core.domain.criteria.ResourceTypeCriteria;
import org.rhq.core.domain.measurement.MeasurementData;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.domain.measurement.ResourceMeasurementScheduleRequest;
import org.rhq.core.domain.plugin.Plugin;
import org.rhq.core.domain.resource.Agent;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.util.PageList;
import org.rhq.core.util.MessageDigestGenerator;
import org.rhq.enterprise.server.drift.DriftServerPluginService;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
import org.rhq.enterprise.server.resource.ResourceTypeManagerLocal;
import org.rhq.enterprise.server.resource.metadata.PluginManagerLocal;
import org.rhq.enterprise.server.test.AbstractEJB3Test;
import org.rhq.enterprise.server.test.TestServerCommunicationsService;
import org.rhq.enterprise.server.test.TransactionCallbackReturnable;
import org.rhq.enterprise.server.util.LookupUtil;
import org.rhq.enterprise.server.util.ServerFactory;
public class UpdatePluginMetadataTestBase extends AbstractEJB3Test {
protected static final String RHQ_SERVER_NAME_PROPERTY_VALUE = "TestServer";
protected TestServerCommunicationsService agentServiceContainer;
protected static final String PLUGIN_NAME = "UpdatePluginMetadataTestBasePlugin"; // don't change this - our test descriptor .xml files use it as plugin name
protected static final String AGENT_NAME = "-dummy agent-";
protected static final String COMMON_PATH_PREFIX = "./test/metadata/";
protected static PluginManagerLocal pluginMgr;
protected static ResourceTypeManagerLocal resourceTypeManager;
protected static ResourceManagerLocal resourceManager;
private Server server;
@Override
protected void beforeMethod() throws Exception {
agentServiceContainer = prepareForTestAgents();
prepareMockAgentServiceContainer();
prepareScheduler();
preparePluginScannerService();
// we perform lookups of config settings from SystemManagerBean.
// SystemManagerBean.getDriftServerPluginManager method requires drift server plugin.
DriftServerPluginService driftServerPluginService = new DriftServerPluginService(getTempDir());
prepareCustomServerPluginService(driftServerPluginService);
driftServerPluginService.masterConfig.getPluginDirectory().mkdirs();
pluginMgr = LookupUtil.getPluginManager();
resourceTypeManager = LookupUtil.getResourceTypeManager();
resourceManager = LookupUtil.getResourceManager();
setServerIdentity(RHQ_SERVER_NAME_PROPERTY_VALUE);
}
@Override
protected void afterMethod() throws Exception {
cleanupTest();
unprepareServerPluginService();
unpreparePluginScannerService();
unprepareScheduler();
unprepareForTestAgents();
deleteServerIdentity();
}
protected void prepareMockAgentServiceContainer() {
agentServiceContainer.measurementService = new MockMeasurementAgentService();
}
protected ResourceType getResourceType(String typeName) {
return getResourceType(typeName, PLUGIN_NAME);
}
protected ResourceType getResourceType(String typeName, String pluginName) {
Subject overlord = getOverlord();
ResourceTypeCriteria resourceTypeCriteria = new ResourceTypeCriteria();
resourceTypeCriteria.setStrict(true);
resourceTypeCriteria.addFilterIgnored(null);
resourceTypeCriteria.addFilterName(typeName);
resourceTypeCriteria.addFilterPluginName(pluginName);
// used in UpdateMeasurementSubsystemTest
resourceTypeCriteria.fetchMetricDefinitions(true);
// used in several UpdateResourceSubsystemTest tests
PageList<ResourceType> results = resourceTypeManager
.findResourceTypesByCriteria(overlord, resourceTypeCriteria);
if (results.size() == 0) {
return null;
} else if (results.size() == 1) {
return results.get(0);
} else {
throw new IllegalStateException("Found more than one resourceType with name " + typeName + " from plugin "
+ pluginName + ".");
}
}
protected Resource createResource(String resourceKey, String name, ResourceType type) {
Resource resource = new Resource(resourceKey, name, type);
resource.setUuid(UUID.randomUUID().toString());
resource.setInventoryStatus(InventoryStatus.COMMITTED);
return resource;
}
protected Resource getResource(ResourceCriteria resourceCriteria) {
Subject overlord = getOverlord();
PageList<Resource> results = resourceManager.findResourcesByCriteria(overlord, resourceCriteria);
if (results.size() == 0) {
return null;
} else if (results.size() == 1) {
return results.get(0);
} else {
throw new IllegalStateException("Found more than one Resource with criteria " + resourceCriteria + ".");
}
}
protected String getSubsystemDirectory() {
return ".";
}
protected void registerPlugin(String pathToDescriptor, String versionOverride) throws Exception {
pathToDescriptor = COMMON_PATH_PREFIX + getSubsystemDirectory() + "/" + pathToDescriptor;
registerPluginInternal(pathToDescriptor, versionOverride);
}
private void registerPluginInternal(String pathToDescriptor, String versionOverride) throws Exception {
System.out.println("Registering plugin with descriptor [" + pathToDescriptor + "]...");
String md5 = MessageDigestGenerator.getDigestString(pathToDescriptor);
Plugin testPlugin = new Plugin(PLUGIN_NAME, "foo.jar", md5);
testPlugin.setDisplayName(PLUGIN_NAME + ": " + pathToDescriptor);
PluginDescriptor descriptor = loadPluginDescriptor(pathToDescriptor);
// if caller passed in their own version, use it - otherwise, use the plugin descriptor version.
// this allows our tests to reuse descriptors without having to duplicate them
if (versionOverride != null) {
testPlugin.setVersion(versionOverride);
} else {
testPlugin.setVersion(descriptor.getVersion());
}
try {
pluginMgr.registerPlugin(testPlugin, descriptor, null, true);
} catch (Throwable t) {
t.printStackTrace();
throw new RuntimeException(t);
}
}
protected void registerPlugin(String pathToDescriptor) throws Exception {
registerPlugin(pathToDescriptor, null);
}
public PluginDescriptor loadPluginDescriptor(String descriptorFile) throws Exception {
URL descriptorUrl = this.getClass().getClassLoader().getResource(descriptorFile);
JAXBContext jaxbContext = JAXBContext.newInstance(DescriptorPackages.PC_PLUGIN);
URL pluginSchemaURL = this.getClass().getClassLoader().getResource("rhq-plugin.xsd");
Schema pluginSchema = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(pluginSchemaURL);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
ValidationEventCollector vec = new ValidationEventCollector();
unmarshaller.setEventHandler(vec);
unmarshaller.setSchema(pluginSchema);
if (descriptorUrl == null)
throw new FileNotFoundException("File " + descriptorFile + " not found");
return (PluginDescriptor) unmarshaller.unmarshal(descriptorUrl.openStream());
}
protected boolean containedIn(String string, String[] references) {
boolean found = false;
for (String ref : references) {
if (string.equals(ref)) {
found = true;
break;
}
}
return found;
}
protected int getPluginId() throws NoResultException {
Plugin existingPlugin;
try {
existingPlugin = (Plugin) em.createNamedQuery(Plugin.QUERY_FIND_BY_NAME).setParameter("name", PLUGIN_NAME)
.getSingleResult();
int plugin1Id = existingPlugin.getId();
return plugin1Id;
} catch (NoResultException nre) {
throw nre;
}
}
protected int getAgentId() throws NoResultException {
try {
Agent existingAgent = getAgent();
int agentId = existingAgent.getId();
return agentId;
} catch (NoResultException nre) {
throw nre;
}
}
protected Agent getAgent() throws NoResultException {
Agent existingAgent;
try {
existingAgent = (Agent) em.createNamedQuery(Agent.QUERY_FIND_BY_NAME).setParameter("name", AGENT_NAME)
.getSingleResult();
return existingAgent;
} catch (NoResultException nre) {
throw nre;
}
}
protected void setUpAgent(Resource testResource) {
Agent agent;
try {
agent = getAgent();
} catch (NoResultException nre) {
agent = new Agent(AGENT_NAME, "localhost", 12345, "http://localhost:12345/", "-dummy token-");
em.persist(agent);
}
testResource.setAgent(agent);
agentServiceContainer.addStartedAgent(agent);
}
protected void createServerIdentity() {
server = ServerFactory.newInstance();
server.setName(RHQ_SERVER_NAME_PROPERTY_VALUE);
server.setAddress("localhost");
server.setPort(7080);
server.setSecurePort(7443);
server.setComputePower(1);
server.setOperationMode(Server.OperationMode.MAINTENANCE);
int serverId = LookupUtil.getServerManager().create(server);
assert serverId > 0 : "could not create our server identity in the DB";
// simulate the agent being "connected" to the server
try {
Agent agent = getAgent();
agent.setServer(server);
LookupUtil.getAgentManager().updateAgent(agent);
} catch (NoResultException nre) {
// no agent to attach
}
}
protected void deleteServerIdentity() throws Exception {
if (server != null) {
cleanupAgent(); // can't remove the server before we purge the agent
LookupUtil.getTopologyManager().deleteServer(LookupUtil.getSubjectManager().getOverlord(), server.getId());
server = null;
}
}
/**
* A dummy that needs to be set up before running ResourceManager.deleteResource()
*/
public class MockMeasurementAgentService implements MeasurementAgentService {
@Override
public Set<MeasurementData> getRealTimeMeasurementValue(int resourceId, Set<MeasurementScheduleRequest> requests) {
return null;
}
public void scheduleCollection(Set<ResourceMeasurementScheduleRequest> resourceSchedules) {
}
public void unscheduleCollection(Set<Integer> resourceIds) {
}
public void updateCollection(Set<ResourceMeasurementScheduleRequest> resourceSchedules) {
}
public Map<String, Object> getMeasurementScheduleInfoForResource(int resourceId) {
return null;
}
}
protected void cleanupTest() throws Exception {
cleanupMetadata();
cleanupAgent();
cleanupPlugin();
}
protected void cleanupMetadata() throws Exception {
String pathToDescriptor = COMMON_PATH_PREFIX + "/noTypes.xml";
registerPluginInternal(pathToDescriptor, null);
}
protected void cleanupAgent() throws Exception {
getTransactionManager().begin();
try {
try {
int agentId = getAgentId();
Agent agent = em.getReference(Agent.class, agentId);
if (null != agent) {
em.remove(agent);
}
} catch (NoResultException nre) {
// ignore, nothing to clean up
}
} catch (Exception e) {
if (Status.STATUS_NO_TRANSACTION != getTransactionManager().getStatus()) {
getTransactionManager().rollback();
}
System.out.println("CANNOT CLEAN UP AGENT: " + e);
throw e;
} finally {
if (Status.STATUS_NO_TRANSACTION != getTransactionManager().getStatus()) {
getTransactionManager().commit();
}
}
}
protected void cleanupPlugin() throws Exception {
getTransactionManager().begin();
try {
try {
int plugin1Id = getPluginId();
Plugin plugin1 = em.getReference(Plugin.class, plugin1Id);
if (null != plugin1) {
em.remove(plugin1);
}
} catch (NoResultException nre) {
// ignore, nothing to do
}
} catch (Exception e) {
if (Status.STATUS_NO_TRANSACTION != getTransactionManager().getStatus()) {
getTransactionManager().rollback();
}
System.out.println("CANNOT CLEAN UP PLUGIN: " + e);
throw e;
} finally {
if (Status.STATUS_NO_TRANSACTION != getTransactionManager().getStatus()) {
getTransactionManager().commit();
}
}
}
protected void cleanupResourceType(String rtName) throws Exception {
try {
ResourceType rt = getResourceType(rtName);
if (null != rt) {
Subject overlord = getOverlord();
ResourceManagerLocal resourceManager = LookupUtil.getResourceManager();
// delete any resources first
ResourceCriteria c = new ResourceCriteria();
c.setStrict(true);
c.addFilterResourceTypeId(rt.getId());
c.addFilterInventoryStatus(InventoryStatus.NEW);
List<Resource> doomedResources = resourceManager.findResourcesByCriteria(overlord, c);
doomedResources.addAll(resourceManager.findResourcesByCriteria(overlord, c));
c.addFilterInventoryStatus(InventoryStatus.UNINVENTORIED);
doomedResources.addAll(resourceManager.findResourcesByCriteria(overlord, c));
// invoke bulk delete on the resource to remove any dependencies not defined in the hibernate entity model
// perform in-band and out-of-band work in quick succession
for (Resource doomed : doomedResources) {
List<Integer> deletedIds = resourceManager.uninventoryResource(overlord, doomed.getId());
for (Integer deletedResourceId : deletedIds) {
resourceManager.uninventoryResourceAsyncWork(overlord, deletedResourceId);
}
}
// Measurement defs go away via cascade remove
getTransactionManager().begin();
rt = em.find(ResourceType.class, rt.getId());
ResourceType parent = rt.getParentResourceTypes().isEmpty() ? null : rt.getParentResourceTypes()
.iterator().next();
em.remove(rt);
if (null != parent) {
em.remove(parent);
}
}
} catch (Exception e) {
if (Status.STATUS_NO_TRANSACTION != getTransactionManager().getStatus()) {
getTransactionManager().rollback();
}
System.out.println("CANNOT CLEAN UP RESOURCE TYPE: " + rtName + ": " + e);
throw e;
} finally {
if (Status.STATUS_NO_TRANSACTION != getTransactionManager().getStatus()) {
getTransactionManager().commit();
}
}
}
//
// provide some convenience methods to create resources
//
protected Subject getOverlord() {
return LookupUtil.getSubjectManager().getOverlord();
}
protected Resource persistNewResource(final String resourceTypeName) throws Exception {
return executeInTransaction(false, new TransactionCallbackReturnable<Resource>() {
public Resource execute() throws Exception {
ResourceType resourceType = getResourceType(resourceTypeName);
Resource resource = new Resource("reskey" + System.currentTimeMillis(), "resname", resourceType);
resource.setUuid(UUID.randomUUID().toString());
resource.setInventoryStatus(InventoryStatus.COMMITTED);
setUpAgent(resource);
em.persist(resource);
return resource;
}
});
}
protected void deleteNewResource(final Resource resource) throws Exception {
try {
List<Integer> deletedIds = resourceManager.uninventoryResource(getOverlord(), resource.getId());
for (Integer deletedResourceId : deletedIds) {
resourceManager.uninventoryResourceAsyncWork(getOverlord(), deletedResourceId);
}
} catch (Exception e) {
System.out.println("CANNOT CLEAN UP RESOURCE: " + resource + ": " + e);
throw e;
}
}
}