package org.epics.archiverappliance.retrieval.cluster; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.net.URLEncoder; import java.sql.Timestamp; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.epics.archiverappliance.Event; import org.epics.archiverappliance.EventStream; import org.epics.archiverappliance.SIOCSetup; import org.epics.archiverappliance.TomcatSetup; import org.epics.archiverappliance.common.TimeUtils; import org.epics.archiverappliance.config.ConfigService; import org.epics.archiverappliance.config.ConfigServiceForTests; import org.epics.archiverappliance.config.PVTypeInfo; import org.epics.archiverappliance.config.persistence.JDBM2Persistence; import org.epics.archiverappliance.retrieval.client.RawDataRetrievalAsEventStream; import org.epics.archiverappliance.utils.ui.GetUrlContent; import org.json.simple.JSONObject; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; /** * Test data retrieval with aliasing and clustering. * We archive a PV with an the real name and then add the alias into the other appliance and then test retrieval * @author mshankar * */ public class ClusterAliasSpanApplianceTest { private static Logger logger = Logger.getLogger(ClusterAliasSpanApplianceTest.class.getName()); File persistenceFolder = new File(ConfigServiceForTests.getDefaultPBTestFolder() + File.separator + "ClusterAliasSpanApplianceTest"); TomcatSetup tomcatSetup = new TomcatSetup(); SIOCSetup siocSetup = new SIOCSetup(); WebDriver driver; @Before public void setUp() throws Exception { if(persistenceFolder.exists()) { FileUtils.deleteDirectory(persistenceFolder); } persistenceFolder.mkdirs(); siocSetup.startSIOCWithDefaultDB(); System.getProperties().put(ConfigService.ARCHAPPL_PERSISTENCE_LAYER, "org.epics.archiverappliance.config.persistence.JDBM2Persistence"); System.getProperties().put(JDBM2Persistence.ARCHAPPL_JDBM2_FILENAME, persistenceFolder.getPath() + File.separator + "testconfig.jdbm2"); tomcatSetup.setUpClusterWithWebApps(this.getClass().getSimpleName(), 2); driver = new FirefoxDriver(); } @After public void tearDown() throws Exception { driver.quit(); tomcatSetup.tearDown(); siocSetup.stopSIOC(); FileUtils.deleteDirectory(persistenceFolder); } @Test public void testSimpleArchivePV() throws Exception { int port = ConfigServiceForTests.RETRIEVAL_TEST_PORT+1; driver.get("http://localhost:" + port + "/mgmt/ui/index.html"); WebElement pvstextarea = driver.findElement(By.id("archstatpVNames")); String pvNameToArchive = "UnitTestNoNamingConvention:sine"; pvstextarea.sendKeys(pvNameToArchive); WebElement archiveButton = driver.findElement(By.id("archstatArchive")); logger.debug("About to submit"); archiveButton.click(); // We have to wait for a few minutes here as it does take a while for the workflow to complete. // In addition, we are also getting .HIHI etc the monitors for which get established many minutes after the beginning of archiving Thread.sleep(10*60*1000); WebElement checkStatusButton = driver.findElement(By.id("archstatCheckStatus")); checkStatusButton.click(); Thread.sleep(2*1000); WebElement statusPVName = driver.findElement(By.cssSelector("#archstatsdiv_table tr:nth-child(1) td:nth-child(1)")); String pvNameObtainedFromTable = statusPVName.getText(); assertTrue("PV Name is not " + pvNameToArchive + "; instead we get " + pvNameObtainedFromTable, pvNameToArchive.equals(pvNameObtainedFromTable)); WebElement statusPVStatus = driver.findElement(By.cssSelector("#archstatsdiv_table tr:nth-child(1) td:nth-child(2)")); String pvArchiveStatusObtainedFromTable = statusPVStatus.getText(); String expectedPVStatus = "Being archived"; assertTrue("Expecting PV archive status to be " + expectedPVStatus + "; instead it is " + pvArchiveStatusObtainedFromTable, expectedPVStatus.equals(pvArchiveStatusObtainedFromTable)); String aapl0 = checkInPersistence("UnitTestNoNamingConvention:sine", 0); String aapl1 = checkInPersistence("UnitTestNoNamingConvention:sine", 1); assertTrue("Expecting the same appliance identity in both typeinfos, instead it is " + aapl0 + " in cluster member 0 and " + aapl1 + " in cluster member 1", aapl0.equals(aapl1)); SIOCSetup.caput("UnitTestNoNamingConvention:sine.HIHI", 2.0); Thread.sleep(2*1000); SIOCSetup.caput("UnitTestNoNamingConvention:sine.HIHI", 3.0); Thread.sleep(2*1000); SIOCSetup.caput("UnitTestNoNamingConvention:sine.HIHI", 4.0); Thread.sleep(2*1000); logger.info("Done updating UnitTestNoNamingConvention:sine.HIHI"); Thread.sleep(2*60*1000); // We have the appliance identity in either aapl0 or aapl1 // Manually add an alias into the other appliance. addAliasIntoOtherAppliance("UnitTestNoNamingConvention:sine", "UnitTestNoNamingConvention:sinealias", aapl0); testRetrievalCount("UnitTestNoNamingConvention:sinealias"); testRetrievalCount("UnitTestNoNamingConvention:sine"); testRetrievalCount("UnitTestNoNamingConvention:sinealias.HIHI"); testRetrievalCount("UnitTestNoNamingConvention:sine.HIHI"); } private String checkInPersistence(String pvName, int clusterIndex) throws Exception { logger.info("Checking for pvtype info in persistence for cluster member " + clusterIndex); String persistenceFile = persistenceFolder.getPath() + File.separator + "testconfig.jdbm2"; String persistenceFileForMember = persistenceFile.replace(".jdbm2", "_appliance" + clusterIndex + ".jdbm2"); System.getProperties().put(JDBM2Persistence.ARCHAPPL_JDBM2_FILENAME, persistenceFileForMember); JDBM2Persistence persistenceLayer = new JDBM2Persistence(); PVTypeInfo typeInfo = persistenceLayer.getTypeInfo(pvName); assertTrue("Expecting the pv typeinfo to be in persistence for cluster member " + clusterIndex, typeInfo != null); return typeInfo.getApplianceIdentity(); } /** * Make sure we get some data when retrieving under the given name * @throws IOException */ private void testRetrievalCount(String pvName) throws IOException { RawDataRetrievalAsEventStream rawDataRetrieval = new RawDataRetrievalAsEventStream("http://localhost:" + ConfigServiceForTests.RETRIEVAL_TEST_PORT+ "/retrieval/data/getData.raw"); Timestamp end = TimeUtils.plusDays(TimeUtils.now(), 3); Timestamp start = TimeUtils.minusDays(end, 6); try(EventStream stream = rawDataRetrieval.getDataForPVS(new String[] { pvName}, start, end, null)) { long previousEpochSeconds = 0; int eventCount = 0; // We are making sure that the stream we get back has times in sequential order... if(stream != null) { for(Event e : stream) { long actualSeconds = e.getEpochSeconds(); assertTrue(actualSeconds >= previousEpochSeconds); previousEpochSeconds = actualSeconds; eventCount++; } } logger.info("Got " + eventCount + " event for pv " + pvName); assertTrue("When asking for data using " + pvName + ", event count is 0. We got " + eventCount, eventCount > 0); } } /** * Manually add an alias entry into the other appliance for this PV. * @param pvName - The name of the pv with the typeinfo * @param aliasName - The name of the alias * @param thisAppliance - The appliance that contains the pvTypeInfo. We'll add the alias into the other appliance */ private void addAliasIntoOtherAppliance(String pvName, String aliasName, String thisAppliance) throws Exception { String otherAppliance = thisAppliance.equals("appliance0") ? "appliance1" : "appliance0"; int clusterIndex = thisAppliance.equals("appliance0") ? 1 : 0; logger.info("Adding alias " + aliasName + " for PV " + pvName + " in appliance " + otherAppliance); int port = ConfigServiceForTests.RETRIEVAL_TEST_PORT + clusterIndex; String addAliasURL = "http://localhost:" + port + "/mgmt/bpl/addAlias?pv=" + URLEncoder.encode(pvName, "UTF-8") + "&aliasname=" + URLEncoder.encode(aliasName, "UTF-8") + "&useThisAppliance=true"; JSONObject status = GetUrlContent.getURLContentAsJSONObject(addAliasURL); logger.info(status.toJSONString()); // Check in persistence for the presence of the alias. // Aliases should ideally be stored in the appliance that contains the PVTypeinfo // In this case, we are intentionally skipping this constraint. // So, we only check in the appliance where we ask to addAlias logger.info("Checking for alias in persistence for cluster member " + clusterIndex); String persistenceFile = persistenceFolder.getPath() + File.separator + "testconfig.jdbm2"; String persistenceFileForMember = persistenceFile.replace(".jdbm2", "_appliance" + clusterIndex + ".jdbm2"); System.getProperties().put(JDBM2Persistence.ARCHAPPL_JDBM2_FILENAME, persistenceFileForMember); JDBM2Persistence persistenceLayer = new JDBM2Persistence(); assertTrue("Alias " + aliasName + " does not have entry in persistence in cluster file " + persistenceFile, pvName.equals(persistenceLayer.getAliasNamesToRealName(aliasName))); } }