/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.rioproject.test.scaling; import net.jini.lookup.ServiceDiscoveryManager; import org.junit.*; import org.junit.runner.RunWith; import org.rioproject.cybernode.Cybernode; import org.rioproject.monitor.ProvisionMonitor; import org.rioproject.test.RioTestRunner; import org.rioproject.test.ServiceMonitor; import org.rioproject.test.SetTestManager; import org.rioproject.test.TestManager; import org.rioproject.test.utils.CybernodeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; /** * The Scaling Service test, verifies that, if an opstring defines a SLA that * states that a service must be scaled up/down when a watchable value breaches * the upper/lower threshold, then Rio actually scales the services up/down * as specified in the opstring. * <p> * The test makes use of the test service and the opstring defining * the test service attributes. The test service is a * <code>SettableLoadService</code>, interface: {@link SettableLoadService}, * implementation: {@link SettableLoadServiceImpl}. * <code>SettableLoadService</code> provides a watchable value - * <code>load</code> - which has no specific meaning but can can be changed * programmatically through the service's interface. The opstring associates * a scaling policy handler with the <code>load</code> watchable value. * <p> * After usual initialization (starting Lookup Service, Provision Monitor, * and Cybernodes and deploying the opstring), the test starts increasing * the total load and verifying that Rio scales the service up as expected. * The test distributes the total load among available test service * instances using the {@link LoadDistributor} utility object. The test * increases the total load gradually, expecting the appearance of one * service instance on each step. The service's <code>MaxPerMachine</code> * parameter is 1, so each new service instance appears on a new Cybernode. * <p> * The test stops increasing the total load when only one Cybernode * remains unoccupied. After that the test stops one of the busy * Cybernodes and checks that Rio increments the service to compensate * the service disappearance. * <p> * Then the test goes in the opposite direction. It decreases the total * load gradually and verifies that Rio scales the service down as expected. * <p> * The test starts one Cybernode on each test host. However, if there are * less than 4 test hosts, the test starts 4 Cybernodes in a round-robin * fashion. */ @RunWith (RioTestRunner.class) public class ScalingServiceTest { static Logger logger = LoggerFactory.getLogger("org.rioproject.test.scaling"); @SetTestManager static TestManager testManager; ServiceMonitor<Cybernode> cyberMon; @BeforeClass public static void init() throws Exception { testManager.startReggie(); testManager.startProvisionMonitor(); ServiceDiscoveryManager sdm = testManager.getServiceDiscoveryManager(); ServiceMonitor<ProvisionMonitor> pmMon = new ServiceMonitor<ProvisionMonitor>(sdm, ProvisionMonitor.class); pmMon.waitFor(1); } @Before public void setup() throws Exception { // 1. START CYBERNODES IN A ROUND-ROBIN FASHION // If there are more than 4 hosts participating in the test, we want // to have one Cybernode running on each host. Otherwise we want to // have 4 Cybernodes in total (in order to have meaningful test). final int N = Math.max(testManager.getHostList().size(), 4); logger.info(""); logger.info("Starting [" + N + "] Cybernodes" + " in a round-robin fashion ..."); for (int i = 0; i < N; i++) { testManager.startCybernode(i); } logger.info("[" + N + "] Cybernodes have been started"); if(cyberMon==null) { ServiceDiscoveryManager sdm = testManager.getServiceDiscoveryManager(); cyberMon = new ServiceMonitor<Cybernode>(sdm, Cybernode.class); } cyberMon.waitFor(N); } @After public void cleanup() { Assert.assertNotNull(cyberMon); Assert.assertNotNull(testManager); for(Object cybernode : cyberMon.getServices()) testManager.stopCybernode(cybernode); Assert.assertEquals((long)0, (long)cyberMon.getCount()); boolean undeployed = testManager.undeploy("Scaling Service Test"); Assert.assertTrue(undeployed); } @Test public void runTests() throws Exception { runTest(); runTest2(); } //@Test public void runTest() throws Exception { logBanner("Running Test #1"); Assert.assertNotNull(testManager); // 2. DEPLOY OPSTRING testManager.deploy(new File("src/test/resources/opstring/scaling_service_test.groovy")); // 3. ASSERTION: ONE SERVICE INSTANCE SHOULD APPEAR ServiceDiscoveryManager sdm = testManager.getServiceDiscoveryManager(); ServiceMonitor<SettableLoadService> servMon = new ServiceMonitor<SettableLoadService>(sdm, SettableLoadService.class); servMon.waitFor(1); Assert.assertEquals((long)1, (long)servMon.getCount()); // 4. GRADUALLY INCREASE LOAD. THE SERVICE SHOULD SCALE UP // The idea is to scale the service up until only one Cybernode // remains unoccupied logger.info(""); logger.info("===> Increasing total load ..."); LoadDistributor loadDistributor = new LoadDistributor(sdm); double desiredServiceLoad = 0.5; int count = cyberMon.getCount(); for (int servicesWanted = 2; servicesWanted < count; servicesWanted++) { // 5. INCREASE LOAD double totalLoad = desiredServiceLoad * servicesWanted; logger.info(""); logger.info("===> New total load: " + totalLoad); loadDistributor.setTotalLoad(totalLoad); // 6. ASSERTION: ONE MORE SERVICE INSTANCE SHOULD APPEAR servMon.waitFor(servicesWanted); } // 7. STOP BUSY CYBERNODE logger.info(""); count = servMon.getCount(); logBanner("Stopping a busy Cybernode ..."); testManager.stopCybernode(CybernodeUtils.findBusy(cyberMon.getServices())); logBanner("A busy Cybernode has been stopped"); // 8. ASSERTION: A SERVICE INSTANCE SHOULD APPEAR ON // THE REMAINING CYBERNODE //servMon.waitFor(count - 1); logBanner("Wait for ["+count+"] services, have ["+servMon.getCount()+"] ..."); servMon.waitFor(count); // 9. GRADUALLY DECREASE LOAD. THE SERVICE SHOULD SCALE DOWN logBanner("Stabilized after Cybernode sutdown, decreasing total load ..."); for (int servicesWanted = count - 1; servicesWanted > 0; servicesWanted--) { // 10. DECREASE LOAD double totalLoad = desiredServiceLoad * servicesWanted; logger.info(""); logger.info("===> New total load: " + totalLoad); loadDistributor.setTotalLoad(totalLoad); // 11. ASSERTION: ONE MORE SERVICE INSTANCE SHOULD DISAPPEAR servMon.waitFor(servicesWanted); } logBanner("Test #1 Complete"); } //@Test public void runTest2() throws Exception { logBanner("Running Test #2"); Assert.assertNotNull(testManager); // 2. DEPLOY OPSTRING testManager.deploy(new File("src/test/resources/opstring/scaling_service_test.groovy")); // 3. ASSERTION: ONE SERVICE INSTANCE SHOULD APPEAR ServiceDiscoveryManager sdm = testManager.getServiceDiscoveryManager(); ServiceMonitor<SettableLoadService> servMon = new ServiceMonitor<SettableLoadService>(sdm, SettableLoadService.class); servMon.waitFor(1); Assert.assertEquals((long)1, (long)servMon.getCount()); // 4. GRADUALLY INCREASE LOAD. THE SERVICE SHOULD SCALE UP // The idea is to scale the service up until only one Cybernode // remains unoccupied logger.info(""); logger.info("===> Increasing total load ..."); LoadDistributor loadDistributor = new LoadDistributor(sdm); double desiredServiceLoad = 0.5; int count = cyberMon.getCount(); for (int servicesWanted = 2; servicesWanted < count; servicesWanted++) { // 5. INCREASE LOAD double totalLoad = desiredServiceLoad * servicesWanted; logger.info(""); logger.info("===> New total load: " + totalLoad); loadDistributor.setTotalLoad(totalLoad, 2); // 6. ASSERTION: ONE MORE SERVICE INSTANCE SHOULD APPEAR servMon.waitFor(servicesWanted); } // 7. STOP BUSY CYBERNODE logger.info(""); count = servMon.getCount(); logBanner("Stopping a busy Cybernode ..."); testManager.stopCybernode(CybernodeUtils.findBusy(cyberMon.getServices())); logBanner("A busy Cybernode has been stopped"); // 8. ASSERTION: A SERVICE INSTANCE SHOULD APPEAR ON // THE REMAINING CYBERNODE //servMon.waitFor(count - 1); logBanner("Wait for ["+count+"] services, have ["+servMon.getCount()+"] ..."); servMon.waitFor(count); // 9. GRADUALLY DECREASE LOAD. THE SERVICE SHOULD SCALE DOWN logBanner("Stabilized after Cybernode sutdown, decreasing total load ..."); for (int servicesWanted = count - 1; servicesWanted > 0; servicesWanted--) { // 10. DECREASE LOAD double totalLoad = desiredServiceLoad * servicesWanted; logger.info(""); logger.info("===> New total load: " + totalLoad); loadDistributor.setTotalLoad(totalLoad, 4); // 11. ASSERTION: ONE MORE SERVICE INSTANCE SHOULD DISAPPEAR servMon.waitFor(servicesWanted); } logBanner("Test #2 Complete"); } private void logBanner(String message) { logger.info("\n************************\n"+message+"\n************************"); } }