/* * 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.measurement.test; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Random; import javax.persistence.Query; import javax.transaction.Status; import org.testng.annotations.Test; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.authz.Permission; import org.rhq.core.domain.authz.Role; import org.rhq.core.domain.criteria.AvailabilityCriteria; import org.rhq.core.domain.discovery.AvailabilityReport; import org.rhq.core.domain.measurement.Availability; import org.rhq.core.domain.measurement.AvailabilityType; import org.rhq.core.domain.measurement.ResourceAvailability; 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.ResourceCategory; import org.rhq.core.domain.resource.ResourceType; import org.rhq.core.domain.resource.group.ResourceGroup; import org.rhq.core.domain.resource.group.composite.ResourceGroupAvailability; import org.rhq.core.domain.resource.group.composite.ResourceGroupComposite.GroupAvailabilityType; import org.rhq.core.domain.util.PageOrdering; import org.rhq.enterprise.server.measurement.AvailabilityManagerLocal; import org.rhq.enterprise.server.measurement.AvailabilityPoint; import org.rhq.enterprise.server.purge.PurgeManagerLocal; import org.rhq.enterprise.server.resource.ResourceAvailabilityManagerLocal; import org.rhq.enterprise.server.resource.ResourceManagerLocal; import org.rhq.enterprise.server.test.AbstractEJB3Test; import org.rhq.enterprise.server.test.TestServerPluginService; import org.rhq.enterprise.server.test.TransactionCallback; import org.rhq.enterprise.server.test.TransactionCallbackReturnable; import org.rhq.enterprise.server.util.LookupUtil; import org.rhq.enterprise.server.util.SessionTestHelper; /** * Test the functionality of the AvailabilityManager * * @author Heiko W. Rupp * @author John Mazzitelli */ public class AvailabilityManagerTest extends AbstractEJB3Test { private static final boolean ENABLE_TESTS = true; private static final AvailabilityType UP = AvailabilityType.UP; private static final AvailabilityType DOWN = AvailabilityType.DOWN; private static final AvailabilityType DISABLED = AvailabilityType.DISABLED; private static final AvailabilityType UNKNOWN = AvailabilityType.UNKNOWN; private AvailabilityManagerLocal availabilityManager; private PurgeManagerLocal purgeManager; private ResourceAvailabilityManagerLocal resourceAvailabilityManager; private ResourceManagerLocal resourceManager; private Subject overlord; private Agent theAgent; private Resource theResource; private ResourceType theResourceType; private List<Resource> additionalResources; private Availability availability1; private Availability availability2; private Availability availability3; private TestServerPluginService testServerPluginService; @Override protected void beforeMethod() { try { prepareScheduler(); this.availabilityManager = LookupUtil.getAvailabilityManager(); this.purgeManager = LookupUtil.getPurgeManager(); this.resourceAvailabilityManager = LookupUtil.getResourceAvailabilityManager(); this.resourceManager = LookupUtil.getResourceManager(); this.overlord = LookupUtil.getSubjectManager().getOverlord(); additionalResources = new ArrayList<Resource>(); testServerPluginService = new TestServerPluginService(getTempDir()); prepareCustomServerPluginService(testServerPluginService); testServerPluginService.masterConfig.getPluginDirectory().mkdirs(); testServerPluginService.startMasterPluginContainer(); } catch (Throwable t) { // Catch RuntimeExceptions and Errors and dump their stack trace, because Surefire will completely swallow them // and throw a cryptic NPE (see http://jira.codehaus.org/browse/SUREFIRE-157)! t.printStackTrace(); throw new RuntimeException(t); } } @Override protected void afterMethod() throws Exception { try { prepareForTestAgents(); if (theResource != null) { // perform in-band and out-of-band work in quick succession // this also deletes our attached agent resourceManager.uninventoryResource(overlord, theResource.getId()); resourceManager.uninventoryResourceAsyncWork(overlord, theResource.getId()); theResource = null; } if (additionalResources != null) { for (Resource res : additionalResources) { resourceManager.uninventoryResource(overlord, res.getId()); resourceManager.uninventoryResourceAsyncWork(overlord, res.getId()); } } if (theResourceType != null) { getTransactionManager().begin(); em.remove(em.find(ResourceType.class, theResourceType.getId())); theResourceType = null; getTransactionManager().commit(); } } catch (Exception e) { e.printStackTrace(); throw e; } finally { unprepareForTestAgents(); unprepareScheduler(); unprepareServerPluginService(); testServerPluginService.stopMasterPluginContainer(); } } @SuppressWarnings("unchecked") @Test(enabled = ENABLE_TESTS) public void testInsertPastAvailabilities() throws Exception { Long now = System.currentTimeMillis(); Long middle = now - 30000; // 30s ago Long then = now - 60000; // 60s ago beginTx(); try { setupResource(); commitAndClose(); Availability aThen = new Availability(theResource, then, UP); aThen.setEndTime(middle); Availability aMiddle = new Availability(theResource, middle, DOWN); aMiddle.setEndTime(now); Availability aNow = new Availability(theResource, now, UP); /* * Simulate a report (aMiddle) that came in late (e.g. because of sorting * issues on the agent or because of a network blip anyway. Expectation is * that it gets just inserted in the middle. */ // UNKNOWN(0) --> persistAvailability(aThen); // UNKNOWN(0) --> UP(-60000) --> persistAvailability(aNow); // no change, already at UP // UNKNOWN(0) --> UP(-60000) --> persistAvailability(aMiddle); // UNKNOWN(0) --> UP(-60000) --> DOWN(-30000) beginTx(); Query q = em.createNamedQuery(Availability.FIND_BY_RESOURCE); q.setParameter("resourceId", theResource.getId()); List<Availability> avails = q.getResultList(); assert avails.size() == 3 : "Did not get 3 availabilities but " + avails.size(); } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } @SuppressWarnings("unchecked") @Test(enabled = ENABLE_TESTS) public void testPurgeAvailabilities() throws Exception { Long now = System.currentTimeMillis(); Long middle = now - 30000; // 30s ago Long then = now - 60000; // 60s ago beginTx(); try { setupResource(); commitAndClose(); Availability aThen = new Availability(theResource, then, UP); Availability aMiddle = new Availability(theResource, middle, DOWN); aMiddle.setEndTime(now); Availability aNow = new Availability(theResource, now, UP); // UNKNOWN(0) --> persistAvailability(aThen); // UNKNOWN(0) --> UP(-60000) --> persistAvailability(aMiddle); // UNKNOWN(0) --> UP(-60000) --> DOWN(-30000) --> persistAvailability(aNow); // UNKNOWN(0) --> UP(-60000) --> DOWN(-30000) --> UP(NOW) --> beginTx(); int purged = purgeManager.purgeAvailabilities(now - 29999); // keeps aMiddle and aNow assert purged == 2 : "Didn't purge 2 --> " + purged; Query q = em.createNamedQuery(Availability.FIND_BY_RESOURCE); q.setParameter("resourceId", theResource.getId()); List<Availability> avails = q.getResultList(); assert avails.size() == 2; assert avails.get(0).getAvailabilityType() == DOWN; assert avails.get(0).getStartTime().equals(middle); assert avails.get(0).getEndTime().equals(now); assert avails.get(1).getAvailabilityType() == UP; assert avails.get(1).getStartTime().equals(now); assert avails.get(1).getEndTime() == null; // try to delete them all - but we never should delete the latest purged = purgeManager.purgeAvailabilities(now + 12345); assert purged == 1 : "Didn't purge 1 --> " + purged; purged = purgeManager.purgeAvailabilities(now + 12345); assert purged == 0 : "Didn't purge 0 --> " + purged; q = em.createNamedQuery(Availability.FIND_BY_RESOURCE); q.setParameter("resourceId", theResource.getId()); avails = q.getResultList(); assert avails.size() == 1; assert avails.get(0).getAvailabilityType() == UP; assert avails.get(0).getStartTime().equals(now); assert avails.get(0).getEndTime() == null; } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } @Test(enabled = ENABLE_TESTS) public void testGetResourceAvailabilities() throws Exception { executeInTransaction(false, new TransactionCallback() { public void execute() throws Exception { setupResource(); } }); // platform: UNKNOWN(0) --> Availability avail; List<Availability> avails; long now = System.currentTimeMillis(); AvailabilityReport report = new AvailabilityReport(false, theAgent.getName()); avails = availabilityManager.getAvailabilitiesForResource(overlord, theResource.getId(), 1, now); assert avails.size() == 1 : "Should only be initial unknown avail data"; assert avails.get(0).getAvailabilityType() == UNKNOWN; assert avails.get(0).getStartTime().equals(1L); assert avails.get(0).getEndTime().equals(now); Long startMillis = 60000L; avail = new Availability(theResource, startMillis, UP); report.addAvailability(avail); availabilityManager.mergeAvailabilityReport(report); // platform: UNKNOWN(0) --> UP(60000) --> avails = availabilityManager.getAvailabilitiesForResource(overlord, theResource.getId(), 30000L, startMillis + 10000); assertEquals("Range should cover both avails", 2, avails.size()); assert avails.get(0).getAvailabilityType() == UNKNOWN; assert avails.get(0).getStartTime().equals(30000L); assert avails.get(0).getEndTime().equals(60000L); assert avails.get(1).getAvailabilityType() == UP; assert avails.get(1).getStartTime().equals(60000L); assert avails.get(1).getEndTime().equals(startMillis + 10000); avails = availabilityManager.getAvailabilitiesForResource(overlord, theResource.getId(), startMillis, startMillis + 10000); assertEquals("Range start on avail start border and extending to future", 1, avails.size()); assert avails.get(0).getAvailabilityType() == UP; assert avails.get(0).getStartTime().equals(startMillis); assert avails.get(0).getEndTime().equals(startMillis + 10000); avails = availabilityManager.getAvailabilitiesForResource(overlord, theResource.getId(), 1000L, startMillis - 10000); assertEquals("Range completely in first avail", 1, avails.size()); assert avails.get(0).getAvailabilityType() == UNKNOWN; assert avails.get(0).getStartTime().equals(1000L); assert avails.get(0).getEndTime().equals(startMillis - 10000); avails = availabilityManager.getAvailabilitiesForResource(overlord, theResource.getId(), startMillis + 5000, startMillis + 10000); assertEquals("Range completely in last avail", 1, avails.size()); assert avails.get(0).getAvailabilityType() == UP; assert avails.get(0).getStartTime().equals(startMillis + 5000); assert avails.get(0).getEndTime().equals(startMillis + 10000); report = new AvailabilityReport(false, theAgent.getName()); // 70000 report.setEnablementReport(true); // simulate a real disable report.addAvailability(new Availability(theResource, (startMillis + 10000L), DISABLED)); availabilityManager.mergeAvailabilityReport(report); // UNKNOWN(0) --> UP(60000) --> DISABLED(70000) --> // before setting other avails, must end disable with enablement report to unknown report = new AvailabilityReport(false, theAgent.getName()); // 75000 report.setEnablementReport(true); report.addAvailability(new Availability(theResource, (startMillis + 15000L), UNKNOWN)); availabilityManager.mergeAvailabilityReport(report); // UNKNOWN(0) --> UP(60000) --> DISABLED(70000) --> UNKNOWN(75000) --> report = new AvailabilityReport(false, theAgent.getName()); // 80000 report.addAvailability(new Availability(theResource, (startMillis + 20000L), UP)); availabilityManager.mergeAvailabilityReport(report); // UNKNOWN(0) --> UP(60000) --> DISABLED(70000) --> UNKNOWN(75000) --> UP(80000) report = new AvailabilityReport(false, theAgent.getName()); // 90000 report.addAvailability(new Availability(theResource, (startMillis + 30000L), DOWN)); availabilityManager.mergeAvailabilityReport(report); // UNKNOWN(0) --> UP(60000) --> DISABLED(70000) --> UNKNOWN(75000) --> UP(80000) --> DOWN(90000) --> avails = availabilityManager.getAvailabilitiesForResource(overlord, theResource.getId(), startMillis - 15000, startMillis + 35000); assertEquals("Range should cover 45000 - 95000", 6, avails.size()); assert avails.get(0).getAvailabilityType() == UNKNOWN; assert avails.get(0).getStartTime().equals(45000L); assert avails.get(0).getEndTime().equals(60000L); assert avails.get(1).getAvailabilityType() == UP; assert avails.get(1).getStartTime().equals(60000L); assert avails.get(1).getEndTime().equals(70000L); assert avails.get(2).getAvailabilityType() == DISABLED; assert avails.get(2).getStartTime().equals(70000L); assert avails.get(2).getEndTime().equals(75000L); assert avails.get(3).getAvailabilityType() == UNKNOWN; assert avails.get(3).getStartTime().equals(75000L); assert avails.get(3).getEndTime().equals(80000L); assert avails.get(4).getAvailabilityType() == UP; assert avails.get(4).getStartTime().equals(80000L); assert avails.get(4).getEndTime().equals(90000L); assert avails.get(5).getAvailabilityType() == DOWN; assert avails.get(5).getStartTime().equals(90000L); assert avails.get(5).getEndTime().equals(95000L); } @Test(enabled = ENABLE_TESTS) public void testGetResourceAvailabilities_2() throws Exception { executeInTransaction(false, new TransactionCallback() { public void execute() throws Exception { setupResource(); } }); // platform: UNKNOWN(0) --> Availability avail; List<Availability> avails; long now = System.currentTimeMillis(); AvailabilityReport report = new AvailabilityReport(false, theAgent.getName()); avails = availabilityManager.getAvailabilitiesForResource(overlord, theResource.getId(), 1, now); assert avails.size() == 1 : "Should only be initial unknown avail data"; assert avails.get(0).getAvailabilityType() == UNKNOWN; assert avails.get(0).getStartTime().equals(1L); assert avails.get(0).getEndTime().equals(now); Long startMillis = 60000L; avail = new Availability(theResource, startMillis, UP); report.addAvailability(avail); availabilityManager.mergeAvailabilityReport(report); // platform: UNKNOWN(0) --> UP(60000) --> avails = availabilityManager.getAvailabilitiesForResource(overlord, theResource.getId(), 30000L, startMillis + 10000); assertEquals("Range should cover both avails", 2, avails.size()); assert avails.get(0).getAvailabilityType() == UNKNOWN; assert avails.get(0).getStartTime().equals(30000L); assert avails.get(0).getEndTime().equals(60000L); assert avails.get(1).getAvailabilityType() == UP; assert avails.get(1).getStartTime().equals(60000L); assert avails.get(1).getEndTime().equals(startMillis + 10000); // purge away all but the most recent avail purgeManager.purgeAvailabilities(System.currentTimeMillis()); // platform: UP(60000) --> avails = availabilityManager.getAvailabilitiesForResource(overlord, theResource.getId(), 10000L, 20000L); assertEquals("all surrogate", 1, avails.size()); assert avails.get(0).getAvailabilityType() == UNKNOWN; assert avails.get(0).getStartTime().equals(10000L); assert avails.get(0).getEndTime().equals(20000L); avails = availabilityManager.getAvailabilitiesForResource(overlord, theResource.getId(), 50000L, 70000L); assertEquals("surrogate to start and then real avail", 2, avails.size()); assert avails.get(0).getAvailabilityType() == UNKNOWN; assert avails.get(0).getStartTime().equals(50000L); assert avails.get(0).getEndTime().equals(60000L); assert avails.get(1).getAvailabilityType() == UP; assert avails.get(1).getStartTime().equals(60000L); assert avails.get(1).getEndTime().equals(70000L); } @Test(enabled = ENABLE_TESTS) public void testGetResourceGroupAvailabilities() throws Exception { executeInTransaction(false, new TransactionCallback() { public void execute() throws Exception { setupResource(); setupAnotherResource(1, theResource); setupAnotherResource(2, theResource); } }); // Create some various group availability situations: // T: 0 10000 20000 30000 40000 // R1: UNKNOWN DOWN UP // R2: UNKNOWN DOWN UP // R3: UNKNOWN DOWN UP DISABLED // ---------------------------------------------------------- // AV: WARN DOWN WARN UP DISABLED final Resource r1 = theResource; final Resource r2 = additionalResources.get(0); final Resource r3 = additionalResources.get(1); AvailabilityReport report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(new Availability(r1, 10000L, DOWN)); report.addAvailability(new Availability(r2, 10000L, DOWN)); report.addAvailability(new Availability(r3, 10000L, DOWN)); report.addAvailability(new Availability(r1, 20000L, UP)); report.addAvailability(new Availability(r2, 20000L, UP)); report.addAvailability(new Availability(r3, 30000L, UP)); report.addAvailability(new Availability(r3, 40000L, DISABLED)); availabilityManager.mergeAvailabilityReport(report); executeInTransaction(new TransactionCallback() { public void execute() throws Exception { // Create the test group final Subject subject = SessionTestHelper.createNewSubject(em, "testSubject"); final Role roleWithSubject = SessionTestHelper .createNewRoleForSubject(em, subject, "role with subject"); roleWithSubject.addPermission(Permission.VIEW_RESOURCE); ResourceGroup group = SessionTestHelper.createNewCompatibleGroupForRole(em, roleWithSubject, "accessible group"); // before adding resources check for EMPTY List<ResourceGroupAvailability> avails; int groupId = group.getId(); avails = availabilityManager.getAvailabilitiesForResourceGroup(overlord, groupId, 0, 20000L); assertEquals("Should have 1 EMPTY rgAvail", 1, avails.size()); assertEquals("Should have 1 EMPTY rgAvail", GroupAvailabilityType.EMPTY, avails.get(0) .getGroupAvailabilityType()); assertEquals("Should cover whole range", 0L, avails.get(0).getStartTime().longValue()); assertEquals("Should cover whole range", 20000L, avails.get(0).getEndTime().longValue()); // add the resources to the group group.addExplicitResource(r1); group.addExplicitResource(r2); group.addExplicitResource(r3); em.merge(group); em.flush(); avails = availabilityManager.getAvailabilitiesForResourceGroup(overlord, groupId, 5000L, 50000); assertEquals("Should have initial avail + 4 changes", 5, avails.size()); assertEquals("AV0 WARN 5000-10000", GroupAvailabilityType.WARN, avails.get(0) .getGroupAvailabilityType()); assertEquals("AV0 WARN 5000-10000", 5000L, avails.get(0).getStartTime().longValue()); assertEquals("AV0 WARN 5000-10000", 10000L, avails.get(0).getEndTime().longValue()); assertEquals("AV1 DOWN 10000-20000", GroupAvailabilityType.DOWN, avails.get(1) .getGroupAvailabilityType()); assertEquals("AV1 DOWN 10000-20000", 10000L, avails.get(1).getStartTime().longValue()); assertEquals("AV1 DOWN 10000-20000", 20000L, avails.get(1).getEndTime().longValue()); assertEquals("AV2 WARN 20000-30000", GroupAvailabilityType.WARN, avails.get(2) .getGroupAvailabilityType()); assertEquals("AV2 WARN 20000-30000", 20000L, avails.get(2).getStartTime().longValue()); assertEquals("AV2 WARN 20000-30000", 30000L, avails.get(2).getEndTime().longValue()); assertEquals("AV3 DISABLED 30000-40000", GroupAvailabilityType.UP, avails.get(3) .getGroupAvailabilityType()); assertEquals("AV3 DISABLED 30000-40000", 30000L, avails.get(3).getStartTime().longValue()); assertEquals("AV3 DISABLED 30000-40000", 40000L, avails.get(3).getEndTime().longValue()); assertEquals("AV4 DISABLED 40000-50000", GroupAvailabilityType.DISABLED, avails.get(4) .getGroupAvailabilityType()); assertEquals("AV4 DISABLED 40000-50000", 40000L, avails.get(4).getStartTime().longValue()); assertEquals("AV4 DISABLED 40000-50000", 50000L, avails.get(4).getEndTime().longValue()); } }); } @Test(enabled = ENABLE_TESTS) @Deprecated // tests deprecated method public void testGetAvailabilities() throws Exception { beginTx(); try { List<AvailabilityPoint> availPoints; Availability avail; setupResource(); // platform: UNKNOWN(0) --> commitAndClose(); AvailabilityReport report = new AvailabilityReport(false, theAgent.getName()); availPoints = availabilityManager.findAvailabilitiesForResource(overlord, theResource.getId(), 1, System.currentTimeMillis(), 3, false); assert availPoints.size() == 3 : "There is no avail data, but should still get 3 availability points"; assert availPoints.get(0).getAvailabilityType() == UNKNOWN; assert availPoints.get(1).getAvailabilityType() == UNKNOWN; assert availPoints.get(2).getAvailabilityType() == UNKNOWN; Long startMillis = 60000L; avail = new Availability(theResource, startMillis, UP); report.addAvailability(avail); availabilityManager.mergeAvailabilityReport(report); // platform: UNKNOWN(0) --> UP(60000) --> // our avail data point is right on the start edge availPoints = availabilityManager.findAvailabilitiesForResource(overlord, theResource.getId(), startMillis, startMillis + 10000, 3, false); assert availPoints.size() == 3 : "There is 1 avail data, but should still get 3 availability points"; assert availPoints.get(0).getAvailabilityType() == UP; assert availPoints.get(1).getAvailabilityType() == UP; assert availPoints.get(2).getAvailabilityType() == UP; // our avail data point is right on the end edge availPoints = availabilityManager.findAvailabilitiesForResource(overlord, theResource.getId(), startMillis - 3, startMillis, 3, false); assert availPoints.size() == 3 : "There is 1 avail data, but should still get 3 availability points"; assert availPoints.get(0).getAvailabilityType() == UNKNOWN; assert !availPoints.get(0).isKnown() : availPoints; assert availPoints.get(1).getAvailabilityType() == UNKNOWN; assert !availPoints.get(1).isKnown() : availPoints; assert availPoints.get(2).getAvailabilityType() == UNKNOWN; assert !availPoints.get(2).isKnown() : availPoints; availPoints = availabilityManager.findAvailabilitiesForResource(overlord, theResource.getId(), startMillis - 20000, startMillis + 10000, 3, false); assert availPoints.size() == 3 : "There is 1 avail data, but should still get 3 availability points"; assert availPoints.get(0).getAvailabilityType() == UNKNOWN; assert !availPoints.get(0).isKnown() : availPoints; assert availPoints.get(1).getAvailabilityType() == UNKNOWN; assert !availPoints.get(1).isKnown() : availPoints; assert availPoints.get(2).getAvailabilityType() == UP : availPoints; availPoints = availabilityManager.findAvailabilitiesForResource(overlord, theResource.getId(), startMillis - 10000, startMillis + 20000, 3, false); assert availPoints.size() == 3 : "There is 1 avail data, but should still get 3 availability points"; assert availPoints.get(0).getAvailabilityType() == UNKNOWN; assert !availPoints.get(0).isKnown() : availPoints; assert availPoints.get(1).getAvailabilityType() == UP; assert availPoints.get(1).isKnown() : availPoints; assert availPoints.get(2).getAvailabilityType() == UP; availPoints = availabilityManager.findAvailabilitiesForResource(overlord, theResource.getId(), startMillis - 20000, startMillis + 20000, 10, false); assert availPoints.size() == 10 : "There is 1 avail data, but should still get 10 availability points"; assert availPoints.get(0).getAvailabilityType() == UNKNOWN : availPoints; assert !availPoints.get(0).isKnown() : availPoints; assert availPoints.get(1).getAvailabilityType() == UNKNOWN : availPoints; assert !availPoints.get(1).isKnown() : availPoints; assert availPoints.get(2).getAvailabilityType() == UNKNOWN : availPoints; assert !availPoints.get(2).isKnown() : availPoints; assert availPoints.get(3).getAvailabilityType() == UNKNOWN : availPoints; assert !availPoints.get(3).isKnown() : availPoints; assert availPoints.get(4).getAvailabilityType() == UNKNOWN : availPoints; assert !availPoints.get(4).isKnown() : availPoints; assert availPoints.get(5).getAvailabilityType() == UP : availPoints; assert availPoints.get(5).isKnown() : availPoints; assert availPoints.get(6).getAvailabilityType() == UP : availPoints; assert availPoints.get(7).getAvailabilityType() == UP : availPoints; assert availPoints.get(8).getAvailabilityType() == UP : availPoints; assert availPoints.get(9).getAvailabilityType() == UP : availPoints; report = new AvailabilityReport(false, theAgent.getName()); // 70000 report.setEnablementReport(true); // simulate a real disable report.addAvailability(new Availability(theResource, (startMillis + 10000L), DISABLED)); availabilityManager.mergeAvailabilityReport(report); // UNKNOWN(0) --> UP(60000) --> DISABLED(70000) --> // before setting other avails, must end disable with enablement report to unknown report = new AvailabilityReport(false, theAgent.getName()); // 75000 report.setEnablementReport(true); report.addAvailability(new Availability(theResource, (startMillis + 15000L), UNKNOWN)); availabilityManager.mergeAvailabilityReport(report); // UNKNOWN(0) --> UP(60000) --> DISABLED(70000) --> UNKNOWN(75000) --> report = new AvailabilityReport(false, theAgent.getName()); // 80000 report.addAvailability(new Availability(theResource, (startMillis + 20000L), UP)); availabilityManager.mergeAvailabilityReport(report); // UNKNOWN(0) --> UP(60000) --> DISABLED(70000) --> UNKNOWN(75000) --> UP(80000) report = new AvailabilityReport(false, theAgent.getName()); // 90000 report.addAvailability(new Availability(theResource, (startMillis + 30000L), DOWN)); availabilityManager.mergeAvailabilityReport(report); // UNKNOWN(0) --> UP(60000) --> DISABLED(70000) --> UNKNOWN(75000) --> UP(80000) --> DOWN(90000) --> availPoints = availabilityManager.findAvailabilitiesForResource(overlord, theResource.getId(), startMillis - 15000, startMillis + 35000, 5, false); // 45000 - 95000 // 45-55 == unknown assert availPoints.size() == 5 : "should get 5 availability points"; assert availPoints.get(0).getAvailabilityType() == UNKNOWN : availPoints; assert !availPoints.get(0).isKnown() : availPoints; // 55-65 == 55-60=unknown, 60-65=up // because part of it was UNKNOWN, we consider the data point UNKNOWN assert availPoints.get(1).getAvailabilityType() == UNKNOWN : availPoints; assert !availPoints.get(1).isKnown() : availPoints; // 65-75 == 65-70=up, 70-75=disabled assert availPoints.get(2).getAvailabilityType() == DISABLED : availPoints; // 75-85 == 75-80=unknown, 80-85=up assert availPoints.get(3).getAvailabilityType() == UNKNOWN : availPoints; // 85-95, == 85-90=up, 90-95=down assert availPoints.get(4).getAvailabilityType() == DOWN : availPoints; // 30000 - 90000 availPoints = availabilityManager.findAvailabilitiesForResource(overlord, theResource.getId(), startMillis - 30000, startMillis + 30000, 10, false); assert availPoints.size() == 10 : "should get 10 availability points"; assert availPoints.get(0).getAvailabilityType() == UNKNOWN : availPoints; // 30-36 assert availPoints.get(1).getAvailabilityType() == UNKNOWN : availPoints; // 36-42 assert availPoints.get(2).getAvailabilityType() == UNKNOWN : availPoints; // 42-48 assert availPoints.get(3).getAvailabilityType() == UNKNOWN : availPoints; // 58-54 assert availPoints.get(4).getAvailabilityType() == UNKNOWN : availPoints; // 54-60 assert availPoints.get(5).getAvailabilityType() == UP : availPoints; // 60-66 assert availPoints.get(6).getAvailabilityType() == DISABLED : availPoints; // 66-72 assert availPoints.get(7).getAvailabilityType() == DISABLED : availPoints; // 72-78 assert availPoints.get(8).getAvailabilityType() == UNKNOWN : availPoints; // 78-84 assert availPoints.get(9).getAvailabilityType() == UP : availPoints; // 84-90 } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } @Test(enabled = ENABLE_TESTS) public void testSetAllAgentResourceAvailabilities() throws Exception { beginTx(); try { setupResource(); commitAndClose(); // setAllAgentResourceAvails will only operate on those that have at least 1 avail row Availability avail = new Availability(theResource, UNKNOWN); persistAvailability(avail); assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == UNKNOWN; availabilityManager.updateAgentResourceAvailabilities(theAgent.getId(), UP, UP); assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == UP; availabilityManager.updateAgentResourceAvailabilities(theAgent.getId(), DOWN, DOWN); assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == DOWN; availabilityManager.updateAgentResourceAvailabilities(theAgent.getId(), DOWN, DOWN); // extend it assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == DOWN; } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } @Test(enabled = ENABLE_TESTS) public void testAgentBackfillNewResource() throws Exception { beginTx(); try { setupResource(); commitAndClose(); // have never heard from the new agent yet - we do not backfill anything in this case LookupUtil.getAgentManager().checkForSuspectAgents(); assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == UNKNOWN; beginTx(); Resource resource = em.find(Resource.class, theResource.getId()); List<Availability> avails = resource.getAvailability(); assert avails != null; assert avails.size() == 1; // the initial avail on resource persist } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } @Test(enabled = ENABLE_TESTS) public void testAgentBackfill() throws Exception { beginTx(); try { prepareForTestAgents(); setupResource(); commitAndClose(); // add a report that says the resource was up 20 minutes ago Availability avail = new Availability(theResource, (System.currentTimeMillis() - 12000000), UP); AvailabilityReport report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); // UNKNOWN(0) --> availabilityManager.mergeAvailabilityReport(report); // UNKNOWN(0) --> UP(-12000000) --> assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == UP; // let's pretend we haven't heard from the agent in a few minutes beginTx(); Agent agent = em.find(Agent.class, theAgent.getId()); agent.setLastAvailabilityPing(System.currentTimeMillis() - (1000 * 60 * 18)); // 18 mins commitAndClose(); // the agent should be suspect and will be considered down, the platform resource should be down // (although children should be UNKNOWN) LookupUtil.getAgentManager().checkForSuspectAgents(); // checks for 5 mins !! // UNKNOWN(0) --> UP(-12000000) -->DOWN(now) --> AvailabilityType curAvail; curAvail = availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()); assert curAvail == DOWN : curAvail; // make sure our resource's new availabilities are consistent (first (UNKNOWN) , second (UP), third (DOWN)) beginTx(); Resource resource = em.find(Resource.class, theResource.getId()); List<Availability> allAvails = resource.getAvailability(); assert allAvails.size() == 3; commitAndClose(); Availability a1 = allAvails.get(0); Availability a2 = allAvails.get(1); Availability a3 = allAvails.get(2); assert a1.getAvailabilityType() == UNKNOWN; assert a2.getAvailabilityType() == UP : a2.getAvailabilityType(); assert a3.getAvailabilityType() == DOWN : a3.getAvailabilityType(); assert a1.getEndTime() != null; assert a2.getEndTime() != null; assert a3.getEndTime() == null; assert a1.getEndTime() > a1.getStartTime(); assert a2.getEndTime() > a2.getStartTime(); assert a2.getStartTime().equals(a1.getEndTime()); assert a3.getStartTime().equals(a2.getEndTime()); } catch (Exception e) { e.printStackTrace(); throw e; } finally { unprepareForTestAgents(); if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } @Test(enabled = true) //ENABLE_TESTS) public void testAgentBackfillPerformance() throws Exception { beginTx(); List<Resource> allResources = new ArrayList<Resource>(); try { prepareForTestAgents(); setupResource(); // setup theResource allResources.add(theResource); // now create a bunch more resources for (int i = 0; i < 99; i++) { allResources.add(setupAnotherResource(i, theResource)); } em.flush(); commitAndClose(); for (Resource res : allResources) { int resId = res.getId(); Availability currentAvailability = availabilityManager.getCurrentAvailabilityForResource(overlord, resId); assert currentAvailability != null : "Current Availability was null for " + resId; assert currentAvailability.getAvailabilityType() == UNKNOWN : "Current AvailabilityType should have been UNKNOWN for " + resId; AvailabilityCriteria c = new AvailabilityCriteria(); c.addFilterResourceId(resId); c.addFilterInterval(0L, Long.MAX_VALUE); c.addSortStartTime(PageOrdering.ASC); List<Availability> allData = availabilityManager.findAvailabilityByCriteria(overlord, c); assert allData != null : "All availabilities was null for " + resId; assert allData.size() == 1 : "All availabilities size was " + allData.size() + " for " + resId; ResourceAvailability currentResAvail = resourceAvailabilityManager.getLatestAvailability(resId); assert currentResAvail != null : "Current ResourceAvailability was null for " + resId; assert currentResAvail.getAvailabilityType() == UNKNOWN : "Current ResourceAvailabilityType should have been UNKNOWN for " + resId; } // let's pretend we haven't heard from the agent in a few minutes beginTx(); Agent agent = em.find(Agent.class, theAgent.getId()); agent.setLastAvailabilityPing(System.currentTimeMillis() - (1000 * 60 * 18)); // 18 mins commitAndClose(); // the agent should be suspect and will be considered down. the resources have their initial // UNKNOWN avails. The platform should get a new DOWN Availability row. The rest should remain // as is since they are already UNKNOWN. long start = System.currentTimeMillis(); LookupUtil.getAgentManager().checkForSuspectAgents(); System.out.println("testAgentBackfillPerformance: checkForSuspectAgents run 1 took " + (System.currentTimeMillis() - start) + "ms"); // add a report that says the resources are now up or disabled- the report will add one avail for each // resource Thread.sleep(500); AvailabilityReport upReport = new AvailabilityReport(false, theAgent.getName()); AvailabilityReport disabledReport = new AvailabilityReport(false, theAgent.getName()); disabledReport.setEnablementReport(true); int resNum = 0; for (Resource resource : allResources) { if (resNum++ <= 80) { upReport.addAvailability(new Availability(resource, UP)); } else { disabledReport.addAvailability(new Availability(resource, DISABLED)); } } start = System.currentTimeMillis(); availabilityManager.mergeAvailabilityReport(upReport); availabilityManager.mergeAvailabilityReport(disabledReport); System.out.println("testAgentBackfillPerformance: mergeAvailabilityReport run took " + (System.currentTimeMillis() - start) + "ms"); // sanity check - make sure the merge at least appeared to work assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == UP; // let's again pretend we haven't heard from the agent in a few minutes beginTx(); agent = em.find(Agent.class, theAgent.getId()); agent.setLastAvailabilityPing(System.currentTimeMillis() - (1000 * 60 * 18)); commitAndClose(); // the agent should be suspect and will be considered down // all of the resources have availabilities now, so another row will be added to them if they are not disabled start = System.currentTimeMillis(); LookupUtil.getAgentManager().checkForSuspectAgents(); System.out.println("testAgentBackfillPerformance: checkForSuspectAgents run 2 took " + (System.currentTimeMillis() - start) + "ms"); AvailabilityType curAvail; start = System.currentTimeMillis(); resNum = 0; for (Resource resource : allResources) { curAvail = availabilityManager.getCurrentAvailabilityTypeForResource(overlord, resource.getId()); AvailabilityType expected = (0 == resNum) ? DOWN : ((resNum > 80) ? DISABLED : UNKNOWN); ++resNum; assert curAvail == expected : "Expected " + expected.name() + " but got " + curAvail.name() + " for " + resource; // make sure our resources' new availabilities are consistent // the first time we backfilled everything was unknown, only the platform was updated. // later we went UP/DISABLED then DOWN so we'll have 2, 3 or 4 rows) beginTx(); resource = em.find(Resource.class, resource.getId()); List<Availability> allAvails = resource.getAvailability(); assert allAvails.size() == ((expected == DOWN) ? 4 : ((expected == DISABLED) ? 2 : 3)) : allAvails; commitAndClose(); Availability a0 = allAvails.get(0); Availability a1 = allAvails.get(1); Availability a2 = null; Availability a3 = null; assert a0.getAvailabilityType() == UNKNOWN : allAvails; switch (expected) { case DOWN: // platform a2 = allAvails.get(2); a3 = allAvails.get(3); assert a1.getAvailabilityType() == DOWN : allAvails; assert a2.getAvailabilityType() == UP : allAvails; assert a3.getAvailabilityType() == DOWN : allAvails; assert a0.getEndTime() != null : allAvails; assert a1.getEndTime() != null : allAvails; assert a2.getEndTime() != null : allAvails; assert a3.getEndTime() == null : allAvails; assert a0.getEndTime() > a0.getStartTime() : allAvails; assert a0.getEndTime().equals(a1.getStartTime()) : allAvails; assert a1.getEndTime().equals(a2.getStartTime()) : allAvails; assert a2.getEndTime().equals(a3.getStartTime()) : allAvails; break; case DISABLED: assert a1.getAvailabilityType() == DISABLED : allAvails; assert a0.getEndTime() != null : allAvails; assert a1.getEndTime() == null : allAvails; assert a0.getEndTime() > a0.getStartTime() : allAvails; assert a0.getEndTime().equals(a1.getStartTime()) : allAvails; break; default: a2 = allAvails.get(2); assert a1.getAvailabilityType() == UP : allAvails; assert allAvails.get(2).getAvailabilityType() == UNKNOWN : allAvails; assert a0.getEndTime() != null : allAvails; assert a1.getEndTime() != null : allAvails; assert a2.getEndTime() == null : allAvails; assert a0.getEndTime() > a0.getStartTime() : allAvails; assert a0.getEndTime().equals(a1.getStartTime()) : allAvails; assert a1.getEndTime().equals(a2.getStartTime()) : allAvails; } } System.out.println("testAgentBackfillPerformance: checking validity of data took " + (System.currentTimeMillis() - start) + "ms"); } catch (Exception e) { e.printStackTrace(); throw e; } finally { unprepareForTestAgents(); if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } @Test(enabled = ENABLE_TESTS) public void testAgentOldReport() throws Exception { beginTx(); try { setupResource(); // inserts initial UNKNOWN Availability at epoch commitAndClose(); Availability avail; long now = System.currentTimeMillis(); // add a report that says the resource is down avail = new Availability(theResource, DOWN); AvailabilityReport report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); availabilityManager.mergeAvailabilityReport(report); // now pretend the agent sent us a report from a previous time period - should insert this in the past avail = new Availability(theResource, (now - 600000), UP); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); availabilityManager.mergeAvailabilityReport(report); assert getPointInTime(new Date(avail.getStartTime() - 2)) == UNKNOWN; assert getPointInTime(new Date(avail.getStartTime())) == UP; assert getPointInTime(new Date(avail.getStartTime() + 2)) == UP; // it's still down though - since we've received a more recent report saying it was down assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == DOWN; // now pretend the agent sent us reports from inbetween our existing time periods // this UP record combines with the UP we added previously avail = new Availability(theResource, (now - 300000), UP); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); availabilityManager.mergeAvailabilityReport(report); assert getPointInTime(new Date(avail.getStartTime() - 2)) == UP; assert getPointInTime(new Date(avail.getStartTime())) == UP; assert getPointInTime(new Date(avail.getStartTime() + 2)) == UP; // its still down though - since we've received a more recent report saying it was down assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == DOWN; // this DOWN record combines with the current DOWN avail = new Availability(theResource, (now - 100000), DOWN); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); availabilityManager.mergeAvailabilityReport(report); assert getPointInTime(new Date(avail.getStartTime() - 2)) == UP; assert getPointInTime(new Date(avail.getStartTime())) == DOWN; assert getPointInTime(new Date(avail.getStartTime() + 2)) == DOWN; // this DOWN record is between the two UPs we added earlier. However, because we are RLE, // we actually lost the information that we had an UP at both -60000 and -30000. We just // have a RLE interval of UP starting at -60000. This new DOWN record will add a new row // that will indicate we were only UP from -60000 to -45000 and DOWN thereafter. This is // an odd test and probably will never occur in the wild (why would an agent tell us // we were one status in the past but another status further back in the past?) avail = new Availability(theResource, (now - 450000), DOWN); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); availabilityManager.mergeAvailabilityReport(report); assert getPointInTime(new Date(avail.getStartTime() - 2)) == UP; assert getPointInTime(new Date(avail.getStartTime())) == DOWN; assert getPointInTime(new Date(avail.getStartTime() + 2)) == DOWN; // its still down assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == DOWN; // let's insert one in the very beginning that is the same type as the current first interval avail = new Availability(theResource, (now - 700000), UP); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); availabilityManager.mergeAvailabilityReport(report); assert getPointInTime(new Date(avail.getStartTime() - 2)) == UNKNOWN; assert getPointInTime(new Date(avail.getStartTime())) == UP; assert getPointInTime(new Date(avail.getStartTime() + 2)) == UP; } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } @Test(enabled = ENABLE_TESTS) public void testAgentCurrentAvailability() throws Exception { beginTx(); try { setupResource(); // inserts initial UNKNOWN Availability at epoch commitAndClose(); Availability avail; long now = System.currentTimeMillis(); // add a report that says the resource is down avail = new Availability(theResource, DOWN); AvailabilityReport report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); availabilityManager.mergeAvailabilityReport(report); // now pretend the agent sent us a report from a previous time period - should insert this in the past avail = new Availability(theResource, (now - 600000), UP); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); availabilityManager.mergeAvailabilityReport(report); assert getPointInTime(new Date(avail.getStartTime() - 2)) == UNKNOWN; //check for current availability. Date unknownTime = new Date(avail.getStartTime() - 2); //make request for avail again but this time requesting most up to date status: should be DOWN List<AvailabilityPoint> list = availabilityManager.findAvailabilitiesForResource(overlord, theResource.getId(), unknownTime.getTime(), unknownTime.getTime() + 1, 1, true); AvailabilityType returnedAvail = list.get(0).getAvailabilityType(); assert returnedAvail == DOWN : "Expected current avail to be '" + DOWN + "' but was '" + returnedAvail + "'."; } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } @Test(enabled = ENABLE_TESTS) public void testAgentOldReport2() throws Exception { beginTx(); try { setupResource(); commitAndClose(); long now = System.currentTimeMillis(); // add a report that says the resource is down - reports can't have the same resource in it twice, // so just create a size=1 report multiple times persistAvailability(new Availability(theResource, (now - 1000000), UP)); persistAvailability(new Availability(theResource, (now - 900000), DOWN)); persistAvailability(new Availability(theResource, (now - 800000), UP)); persistAvailability(new Availability(theResource, (now - 50000), DOWN)); persistAvailability(new Availability(theResource, (now - 30000), UP)); persistAvailability(new Availability(theResource, (now), DOWN)); // now pretend the agent sent us a report from a previous time period - should insert this in the past persistAvailability(new Availability(theResource, (now - 600000), UP)); // its still down though - since we've received a more recent report saying it was down assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == DOWN; // now pretend the agent sent us reports from in between our existing time periods // this UP record combines with the UP we added previously persistAvailability(new Availability(theResource, (now - 300000), UP)); // its still down though - since we've received a more recent report saying it was down assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == DOWN; // this DOWN record combines with the current DOWN persistAvailability(new Availability(theResource, (now - 100000), DOWN)); // this DOWN record is between the two UPs we added earlier. However, because we are RLE, // we actually lost the information that we had an UP at both -60000 and -30000. We just // have a RLE interval of UP starting at -60000. This new DOWN record will add a new row // that will indicate we were only UP fro -60000 to -45000 and DOWN thereafter. This is // an odd test and probably will never occur in the wild (why would an agent tell us // we were one status in the past but another status further back in the past?) persistAvailability(new Availability(theResource, (now - 450000), DOWN)); // its still down assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == DOWN; // let's insert one in the very beginning that is the same type as the current first interval persistAvailability(new Availability(theResource, (now - 700000), UP)); } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } @Test(enabled = ENABLE_TESTS) @Deprecated // tests deprecated method public void testGetAvailabilities2() throws Exception { beginTx(); try { Availability avail; setupResource(); commitAndClose(); Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, 2000); cal.set(Calendar.MONTH, 0); cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.HOUR, 1); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); Date date1 = cal.getTime(); cal.set(Calendar.HOUR, 1); cal.set(Calendar.MINUTE, 30); Date date2 = cal.getTime(); cal.set(Calendar.HOUR, 2); cal.set(Calendar.MINUTE, 0); Date date3 = cal.getTime(); cal.set(Calendar.HOUR, 2); cal.set(Calendar.MINUTE, 30); Date date4 = cal.getTime(); cal.set(Calendar.HOUR, 3); cal.set(Calendar.MINUTE, 0); Date date5 = cal.getTime(); cal.set(Calendar.HOUR, 3); cal.set(Calendar.MINUTE, 30); Date date6 = cal.getTime(); avail = new Availability(theResource, date1.getTime(), UP); avail.setEndTime(date2.getTime()); persistAvailability(avail); avail = new Availability(theResource, date2.getTime(), DOWN); avail.setEndTime(date3.getTime()); persistAvailability(avail); avail = new Availability(theResource, date3.getTime(), UP); avail.setEndTime(date4.getTime()); persistAvailability(avail); avail = new Availability(theResource, date4.getTime(), DOWN); avail.setEndTime(date5.getTime()); persistAvailability(avail); avail = new Availability(theResource, date5.getTime(), UP); avail.setEndTime(date6.getTime()); persistAvailability(avail); avail = new Availability(theResource, date6.getTime(), DOWN); persistAvailability(avail); List<AvailabilityPoint> points = availabilityManager.findAvailabilitiesForResource(overlord, theResource.getId(), date1.getTime(), date6.getTime(), 5, false); assert points.size() == 5; assert points.get(0).getAvailabilityType() == UP; assert points.get(1).getAvailabilityType() == DOWN; assert points.get(2).getAvailabilityType() == UP; assert points.get(3).getAvailabilityType() == DOWN; assert points.get(4).getAvailabilityType() == UP; } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } /** * See if merging in AvailabilityReports from the agent work. * * @throws Exception in case of error */ @Test(enabled = ENABLE_TESTS) public void testMergeReport() throws Exception { beginTx(); try { Availability avail; AvailabilityReport report; setupResource(); commitAndClose(); long allAvailCount = setUpAvailabilities(); // we now have 1:00 UP, 1:20 DOWN, 1:40 UP Subject overlord = LookupUtil.getSubjectManager().getOverlord(); avail = availabilityManager.getCurrentAvailabilityForResource(overlord, theResource.getId()); assert avail.getAvailabilityType() == UP; assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == UP; // add something after the start of last, but still be UP (result: nothing added) Long currentStartTime = avail.getStartTime(); avail = new Availability(theResource, (currentStartTime + 3600000), UP); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); Thread.sleep(1000); availabilityManager.mergeAvailabilityReport(report); // the agent should have been updated, but no new rows in availability were added Agent agent = LookupUtil.getAgentManager().getAgentByName(theAgent.getName()); Date lastReport = new Date(agent.getLastAvailabilityReport()); assert lastReport != null; assert countAvailabilitiesInDB().equals(allAvailCount); avail = availabilityManager.getCurrentAvailabilityForResource(overlord, theResource.getId()); // should have returned availability3 // NOTE: availability3 never got an ID assigned, so we can't compare by id // assert avail.getId() == availability3.getId(); assert avail.getStartTime().equals(availability3.getStartTime()); assert avail.getAvailabilityType() == availability3.getAvailabilityType(); assert Math.abs(avail.getStartTime() - availability3.getStartTime()) < 1000; assert avail.getEndTime() == null; assert availability3.getEndTime() == null; // change start after the start of last (result: add new avail row) avail = new Availability(theResource, (currentStartTime + 7200000), DOWN); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); Thread.sleep(1000); availabilityManager.mergeAvailabilityReport(report); // the agent should have been updated and a new row in availability was added (resource is now DOWN) agent = LookupUtil.getAgentManager().getAgentByName(theAgent.getName()); assert new Date(agent.getLastAvailabilityReport()).after(lastReport); assert countAvailabilitiesInDB().equals(allAvailCount + 1); assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == DOWN; Availability queriedAvail = availabilityManager.getCurrentAvailabilityForResource(overlord, theResource.getId()); assert queriedAvail.getId() > 0; assert queriedAvail.getAvailabilityType() == avail.getAvailabilityType(); assert Math.abs(queriedAvail.getStartTime() - avail.getStartTime()) < 1000; assert queriedAvail.getEndTime() == null; assert avail.getEndTime() == null; } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } /** * See if merging in AvailabilityReports from the agent work if it includes a stale resource * * @throws Exception in case of error */ @Test(enabled = ENABLE_TESTS) public void testMergeReportWithStaleResource() throws Exception { beginTx(); try { Availability avail; AvailabilityReport report; setupResource(); commitAndClose(); long allAvailCount = setUpAvailabilities(); // we now have 1:00 UP, 1:20 DOWN, 1:40 UP Subject overlord = LookupUtil.getSubjectManager().getOverlord(); avail = availabilityManager.getCurrentAvailabilityForResource(overlord, theResource.getId()); assert avail.getAvailabilityType() == UP; assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == UP; // add something after the start of last, but still be UP (result: nothing added) Long currentStartTime = avail.getStartTime(); avail = new Availability(theResource, (currentStartTime + 3600000), UP); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); // add something with a stale, non-existent resource. It should be ignored, logging a message, // and the rest of the report should be processed. avail = new Availability(new Resource(898989), (currentStartTime + 3600000), UP); report.addAvailability(avail); Thread.sleep(1000); availabilityManager.mergeAvailabilityReport(report); // the agent should have been updated, but no new rows in availability were added Agent agent = LookupUtil.getAgentManager().getAgentByName(theAgent.getName()); Date lastReport = new Date(agent.getLastAvailabilityReport()); assert lastReport != null; assert countAvailabilitiesInDB().equals(allAvailCount); avail = availabilityManager.getCurrentAvailabilityForResource(overlord, theResource.getId()); // should have returned availability3 // NOTE: availability3 never got an ID assigned, so we can't compare by id // assert avail.getId() == availability3.getId(); assert avail.getStartTime().equals(availability3.getStartTime()); assert avail.getAvailabilityType() == availability3.getAvailabilityType(); assert Math.abs(avail.getStartTime() - availability3.getStartTime()) < 1000; assert avail.getEndTime() == null; assert availability3.getEndTime() == null; // change start after the start of last (result: add new avail row) avail = new Availability(theResource, (currentStartTime + 7200000), DOWN); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); Thread.sleep(1000); availabilityManager.mergeAvailabilityReport(report); // the agent should have been updated and a new row in availability was added (resource is now DOWN) agent = LookupUtil.getAgentManager().getAgentByName(theAgent.getName()); assert new Date(agent.getLastAvailabilityReport()).after(lastReport); assert countAvailabilitiesInDB().equals(allAvailCount + 1); assert availabilityManager.getCurrentAvailabilityTypeForResource(overlord, theResource.getId()) == DOWN; Availability queriedAvail = availabilityManager.getCurrentAvailabilityForResource(overlord, theResource.getId()); assert queriedAvail.getId() > 0; assert queriedAvail.getAvailabilityType() == avail.getAvailabilityType(); assert Math.abs(queriedAvail.getStartTime() - avail.getStartTime()) < 1000; assert queriedAvail.getEndTime() == null; assert avail.getEndTime() == null; } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } /** * This was an attempt to get the behavior of this bug but it didn't. This inserts duplicate avail records, which * should be discarded. */ @Test(enabled = ENABLE_TESTS) public void testBZ884338_1() throws Exception { beginTx(); try { Availability avail; AvailabilityReport report; setupResource(); commitAndClose(); long allAvailCount = setUpAvailabilities(); assertTrue("should have >= 3 avail records", 3 <= allAvailCount); List<Availability> avails = getResourceAvailabilities(theResource); // there is always the UNKNOWN period starting at epoch, plus the three created in setup assertEquals(avails.toString(), 4, avails.size()); // we now have 1:00 UP, 1:20 DOWN, 1:40 UP avail = availabilityManager.getCurrentAvailabilityForResource(overlord, theResource.getId()); assert avail.getAvailabilityType() == UP; // insert a duplicate current 1:40 UP and an out-of-order 1:20 DOWN, these should be ignored Long currentStartTime = avail.getStartTime(); long newStartTime = currentStartTime; avail = new Availability(theResource, newStartTime, UP); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); newStartTime = (currentStartTime - (20 * 60 * 1000L)); avail = new Availability(theResource, newStartTime, DOWN); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); Thread.sleep(1000); availabilityManager.mergeAvailabilityReport(report); // the agent should have been updated, but no new rows in availability were added Agent agent = LookupUtil.getAgentManager().getAgentByName(theAgent.getName()); Date lastReport = new Date(agent.getLastAvailabilityReport()); assert lastReport != null; assertEquals(allAvailCount, countAvailabilitiesInDB().longValue()); avails = getResourceAvailabilities(theResource); assertEquals(avails.toString(), 4, avails.size()); // avail start times should be unchanged 0, 1:00 (UP), 1:20(DOWN), 1:40(UP) avail = avails.get(0); // 0..1:00 assertTrue(avail.toString(), Math.abs(avail.getStartTime() - 0L) < 1000L); assertEquals(avail.toString(), AvailabilityType.UNKNOWN, avail.getAvailabilityType()); assertTrue(avail.toString(), Math.abs(avail.getEndTime() - avails.get(1).getStartTime()) < 1000L); avail = avails.get(1); // 1:00..1:20 assertTrue(avail.toString(), Math.abs(currentStartTime - (avail.getStartTime() + (40 * 60 * 1000))) < 1000L); assertEquals(avail.toString(), AvailabilityType.UP, avail.getAvailabilityType()); assertTrue(avail.toString(), Math.abs(avail.getEndTime() - avails.get(2).getStartTime()) < 1000L); avail = avails.get(2); // 1:20..1:40 assertTrue(avail.toString(), Math.abs(currentStartTime - (avail.getStartTime() + (20 * 60 * 1000))) < 1000L); assertEquals(avail.toString(), AvailabilityType.DOWN, avail.getAvailabilityType()); assertTrue(avail.toString(), Math.abs(avail.getEndTime() - avails.get(3).getStartTime()) < 1000L); avail = avails.get(3); // 1:40 assertTrue(avail.toString(), Math.abs(currentStartTime - avail.getStartTime()) < 1000L); assertEquals(avail.toString(), AvailabilityType.UP, avail.getAvailabilityType()); assertEquals(avail.toString(), avail.getEndTime(), null); } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } /** * Still can't reproduce the bug but this test tries to validate the repair code we have in case we detect * the problem. */ @Test(enabled = ENABLE_TESTS) public void testBZ884338_2() throws Exception { beginTx(); try { Availability avail; AvailabilityReport report; setupResource(); commitAndClose(); long allAvailCount = setUpAvailabilities(); assertTrue("should have >= 3 avail records", 3 <= allAvailCount); List<Availability> avails = getResourceAvailabilities(theResource); // there is always the UNKNOWN period starting at epoch, plus the three created in setup assertEquals(avails.toString(), 4, avails.size()); // we now have 1:00 UP, 1:20 DOWN, 1:40 UP avail = availabilityManager.getCurrentAvailabilityForResource(overlord, theResource.getId()); beginTx(); avail = em.find(Availability.class, avail.getId()); assert avail.getAvailabilityType() == UP; // mess things up by assigning an end time to the latest avail record. Long currentStartTime = avail.getStartTime(); Long nonNullEndTime = currentStartTime + 1000L; avail.setEndTime(nonNullEndTime); avail = em.merge(avail); commitAndClose(); // try to insert new avail, this should trigger the repair code long newStartTime = (currentStartTime + (5 * 60 * 1000L)); avail = new Availability(theResource, newStartTime, DOWN); report = new AvailabilityReport(false, theAgent.getName()); report.addAvailability(avail); Thread.sleep(1000); availabilityManager.mergeAvailabilityReport(report); // the end time of avail 4 should have been reset to the start time of avail 5. Avail 5 should have been // added and should be DOWN avails = getResourceAvailabilities(theResource); assertEquals(avails.toString(), 5, avails.size()); // avail start times should now be 0, 1:00 (UP), 1:20(DOWN), 1:40(UP), 1:45(DOWN) avail = avails.get(0); // 0..1:00 assertTrue(avail.toString(), Math.abs(avail.getStartTime() - 0L) < 1000L); assertEquals(avail.toString(), AvailabilityType.UNKNOWN, avail.getAvailabilityType()); assertTrue(avail.toString(), Math.abs(avail.getEndTime() - avails.get(1).getStartTime()) < 1000L); avail = avails.get(1); // 1:00..1:20 assertTrue(avail.toString(), Math.abs(newStartTime - (avail.getStartTime() + (45 * 60 * 1000))) < 1000L); assertEquals(avail.toString(), AvailabilityType.UP, avail.getAvailabilityType()); assertTrue(avail.toString(), Math.abs(avail.getEndTime() - avails.get(2).getStartTime()) < 1000L); avail = avails.get(2); // 1:20..1:40 assertTrue(avail.toString(), Math.abs(newStartTime - (avail.getStartTime() + (25 * 60 * 1000))) < 1000L); assertEquals(avail.toString(), AvailabilityType.DOWN, avail.getAvailabilityType()); assertTrue(avail.toString(), Math.abs(avail.getEndTime() - avails.get(3).getStartTime()) < 1000L); avail = avails.get(3); // 1:40..1:45 assertTrue(avail.toString(), Math.abs(newStartTime - (avail.getStartTime() + (5 * 60 * 1000))) < 1000L); assertEquals(avail.toString(), AvailabilityType.UP, avail.getAvailabilityType()); assertTrue(avail.toString(), Math.abs(avail.getEndTime() - avails.get(4).getStartTime()) < 1000L); // THE FIX avail = avails.get(4); // 1:45..null assertTrue(avail.toString(), Math.abs(newStartTime - avail.getStartTime()) < 1000L); assertEquals(avail.toString(), AvailabilityType.DOWN, avail.getAvailabilityType()); assertEquals(avail.toString(), null, avail.getEndTime()); } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } @Test(enabled = ENABLE_TESTS) public void testMergeReportPerformance() throws Exception { beginTx(); List<Resource> allResources = new ArrayList<Resource>(); try { setupResource(); // setup theResource allResources.add(theResource); // now create a bunch more resources for (int i = 0; i < 100; i++) { allResources.add(setupAnotherResource(i, theResource)); } em.flush(); commitAndClose(); // add a report that says the resources are now up - the report will add one avail for each resource // at this point, the resources do not yet have a row in availability - after the merge they will have 1 AvailabilityReport report = new AvailabilityReport(false, theAgent.getName()); for (Resource resource : allResources) { Availability avail = new Availability(resource, UP); report.addAvailability(avail); } long start = System.currentTimeMillis(); availabilityManager.mergeAvailabilityReport(report); System.out.println("testMergeReportPerformance: mergeAvailabilityReport run 1 took " + (System.currentTimeMillis() - start) + "ms"); AvailabilityType curAvail; start = System.currentTimeMillis(); for (Resource resource : allResources) { curAvail = availabilityManager.getCurrentAvailabilityTypeForResource(overlord, resource.getId()); assert curAvail == UP : curAvail; } System.out.println("testMergeReportPerformance: checking validity of data 1 took " + (System.currentTimeMillis() - start) + "ms"); // add a report that says the resources are now down - the report will add one avail for each resource // at this point, the resources have 1 row in availability - after the merge they will have 2 report = new AvailabilityReport(false, theAgent.getName()); for (Resource resource : allResources) { Availability avail = new Availability(resource, DOWN); report.addAvailability(avail); } start = System.currentTimeMillis(); availabilityManager.mergeAvailabilityReport(report); System.out.println("testMergeReportPerformance: mergeAvailabilityReport run 2 took " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (Resource resource : allResources) { curAvail = availabilityManager.getCurrentAvailabilityTypeForResource(overlord, resource.getId()); assert curAvail == DOWN : curAvail; } System.out.println("testMergeReportPerformance: checking validity of data 2 took " + (System.currentTimeMillis() - start) + "ms"); // add a report that says the resources are now unknown - the report will add one avail for each resource // at this point, the resources have 2 rows in availability - after the merge they will have 3 report = new AvailabilityReport(false, theAgent.getName()); for (Resource resource : allResources) { Availability avail = new Availability(resource, UNKNOWN); report.addAvailability(avail); } start = System.currentTimeMillis(); availabilityManager.mergeAvailabilityReport(report); System.out.println("testMergeReportPerformance: mergeAvailabilityReport run 3 took " + (System.currentTimeMillis() - start) + "ms"); start = System.currentTimeMillis(); for (Resource resource : allResources) { curAvail = availabilityManager.getCurrentAvailabilityTypeForResource(overlord, resource.getId()); assert curAvail == UNKNOWN : curAvail; } System.out.println("testMergeReportPerformance: checking validity of data 3 took " + (System.currentTimeMillis() - start) + "ms"); } catch (Exception e) { e.printStackTrace(); throw e; } finally { if (Status.STATUS_ACTIVE == getTransactionManager().getStatus()) { getTransactionManager().rollback(); } } } private void beginTx() throws Exception { getTransactionManager().begin(); } private void commitAndClose() throws Exception { getTransactionManager().commit(); } /** * @param time The point in time will span 1 millisecond from the time provided to time+1 * @return */ private AvailabilityType getPointInTime(Date time) { List<AvailabilityPoint> list = availabilityManager.findAvailabilitiesForResource(overlord, theResource.getId(), time.getTime(), time.getTime() + 1, 1, false); assert list != null; assert list.size() == 1 : "Should have returned a single point"; AvailabilityType type = list.get(0).getAvailabilityType(); switch (type) { case UP: case DOWN: case DISABLED: case UNKNOWN: return type; default: assert false : "AvailabilityType enum has some additional values not known to this test: " + type; } return null; } /** * See how many rows we have in the availability table * * @return the rowcount * * @throws Exception */ private Long countAvailabilitiesInDB() throws Exception { return executeInTransaction(new TransactionCallbackReturnable<Long>() { public Long execute() throws Exception { Query q = em.createQuery("SELECT count(a) FROM Availability a"); Long count = (Long) q.getSingleResult(); return count; } }); } /** * See how many rows we have in the availability table * * @return the rowcount * * @throws Exception */ private List<Availability> getResourceAvailabilities(Resource r) throws Exception { AvailabilityCriteria c = new AvailabilityCriteria(); c.addFilterResourceId(r.getId()); c.addSortStartTime(PageOrdering.ASC); return availabilityManager.findAvailabilityByCriteria(overlord, c); } /** * Just set up a resource where we can attach the availabilities to * * @return A Resource ready to use */ private Resource setupResource() { String tuid = "" + new Random().nextInt(); String prefix = this.getClass().getSimpleName() + "_" + tuid + "_"; theAgent = new Agent(prefix + "agent", "localhost" + tuid, 1234, "", "randomToken" + tuid); em.persist(theAgent); theResourceType = new ResourceType(prefix + "type", prefix + "plugin", ResourceCategory.PLATFORM, null); em.persist(theResourceType); theResource = new Resource(prefix + "resourceKey", prefix + "resourceName", theResourceType); theResource.setUuid("" + new Random().nextInt()); theResource.setAgent(theAgent); theResource.setInventoryStatus(InventoryStatus.COMMITTED); em.persist(theResource); em.flush(); return theResource; } /** * Set up another unique resource that will be related to <code>theAgent</code>. The resource will be of type <code> * theResourceType</code>. * * @param uniqueNumber used to define a unique key for the resource * * @return A Resource ready to use */ private Resource setupAnotherResource(int uniqueNumber, Resource parentResource) { Resource newResource; newResource = new Resource("test-platform-key-" + uniqueNumber, "test-platform-name-" + uniqueNumber, theResourceType); newResource.setUuid("" + new Random().nextInt()); newResource.setAgent(theAgent); newResource.setInventoryStatus(InventoryStatus.COMMITTED); if (null != parentResource) { parentResource.addChildResource(newResource); } em.persist(newResource); additionalResources.add(newResource); return newResource; } /** * Set up an availability scenario where we set up availability for one hour, split it in the middle and have 20min * up, 20min down, 20min up starting at 1:00am. * * @return total number of availability records in the DB after we've added ours * * @throws Exception */ private long setUpAvailabilities() throws Exception { Calendar cal = Calendar.getInstance(); cal.setTime(new Date()); cal.set(Calendar.HOUR, 1); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); Date start = cal.getTime(); // split time cal.set(Calendar.HOUR, 1); cal.set(Calendar.MINUTE, 20); Date splitStart = cal.getTime(); cal.set(Calendar.MINUTE, 40); Date splitEnd = cal.getTime(); long count = countAvailabilitiesInDB(); availability1 = new Availability(theResource, start.getTime(), UP); availability1.setEndTime(splitStart.getTime()); persistAvailability(availability1); availability2 = new Availability(theResource, splitStart.getTime(), DOWN); availability2.setEndTime(splitEnd.getTime()); persistAvailability(availability2); availability3 = new Availability(theResource, splitEnd.getTime(), UP); persistAvailability(availability3); long countNow = countAvailabilitiesInDB(); assert countNow == (count + 3) : "Did not find three availabilities - instead found: " + countNow; return countNow; } /** * Convenience method for persisting availability. Availability data can no longer be directly merged * by the EntityManager because it does not update the corresponding currentAvailability data on the * Resource entity. This method will update the necessary objects for you. */ private void persistAvailability(Availability... availabilities) { AvailabilityReport report = new AvailabilityReport(false, theAgent.getName()); for (Availability avail : availabilities) { report.addAvailability(avail); } availabilityManager.mergeAvailabilityReport(report); } }