/*
* Copyright 2014-2016 CyberVision, Inc.
*
* 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.kaaproject.kaa.server.control.service.loadmgmt;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.apache.thrift.TMultiplexedProcessor;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.server.TThreadPoolServer.Args;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;
import org.apache.thrift.transport.TTransportException;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.kaaproject.kaa.server.common.thrift.KaaThriftService;
import org.kaaproject.kaa.server.common.thrift.gen.bootstrap.BootstrapThriftService;
import org.kaaproject.kaa.server.common.zk.control.ControlNode;
import org.kaaproject.kaa.server.common.zk.gen.BootstrapNodeInfo;
import org.kaaproject.kaa.server.common.zk.gen.ConnectionInfo;
import org.kaaproject.kaa.server.common.zk.gen.LoadInfo;
import org.kaaproject.kaa.server.common.zk.gen.OperationsNodeInfo;
import org.kaaproject.kaa.server.common.zk.gen.TransportMetaData;
import org.kaaproject.kaa.server.control.service.loadmgmt.dynamicmgmt.EndpointCountRebalancer;
import org.kaaproject.kaa.server.control.service.zk.ControlZkService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* DynamicLoadManager Integration test, emulates new Bootstrap node adding.
*
* @author Andrey Panasenko
*/
public class TestDynamicLoadManagerIT {
private static final int DEFAULT_PRIORITY = 10;
/**
* The Constant LOG.
*/
private static final Logger LOG = LoggerFactory.getLogger(TestDynamicLoadManagerIT.class);
/**
* Thrift host for Bootstrap test service
*/
private static final String thriftHost = "localhost";
/**
* Thrift port for Bootstrap test service
*/
private static final int thriftPort = 9819;
/**
* Thread executor
*/
private static ExecutorService executor = null;
private static LoadDistributionService ldServiceMock;
private static ControlZkService zkServiceMock;
private static ControlNode pNodeMock;
/**
* Bootstrap thrift test service runner
*/
private ThriftRunner bootstrapThrift;
/**
* Initialize mock objects and necessary test services
*/
@BeforeClass
public static void init() throws Exception {
executor = Executors.newCachedThreadPool();
ldServiceMock = mock(LoadDistributionService.class);
zkServiceMock = mock(ControlZkService.class);
pNodeMock = mock(ControlNode.class);
when(ldServiceMock.getOpsServerHistoryTtl()).thenReturn(300);
when(ldServiceMock.getRebalancer()).thenReturn(new EndpointCountRebalancer());
when(ldServiceMock.getZkService()).thenReturn(zkServiceMock);
when(zkServiceMock.getControlZkNode()).thenReturn(pNodeMock);
}
/**
* Stops services.
*/
@AfterClass
public static void after() throws Exception {
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
}
/**
* Start Bootstrap thrift service
*/
@Before
public void beforeTest() throws Exception {
bootstrapThrift = new ThriftRunner(thriftHost, thriftPort);
executor.execute(bootstrapThrift);
bootstrapThrift.waitStart();
}
/**
* Stop bootstrap hrift service
*/
@After
public void afterTest() throws Exception {
bootstrapThrift.shutdown();
}
/**
* Test Bootstrap node add.
*/
@Test
public void bootstrapNodeAddTest() {
LOG.info("bootstrapNodeAddTest started");
DynamicLoadManager dm = getDynamicLoadManager();
ConnectionInfo bsConnectionInfo = new ConnectionInfo(thriftHost, thriftPort, ByteBuffer.wrap("Just array".getBytes()));
BootstrapNodeInfo bsNode = getBootstrapNodeInfo(bsConnectionInfo);
dm.onNodeAdded(bsNode);
checkBSNode();
}
/**
* Test Bootstrap Node update
*/
@Test
public void bootstrapNodeUpdateTest() {
LOG.info("BootstrapNodeUpdateTest started");
DynamicLoadManager dm = getDynamicLoadManager();
ConnectionInfo bsErrConnectionInfo = new ConnectionInfo(thriftHost, thriftPort + 1, ByteBuffer.wrap("Just array".getBytes()));
BootstrapNodeInfo bsErrNode = getBootstrapNodeInfo(bsErrConnectionInfo);
dm.onNodeAdded(bsErrNode);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
fail(e.toString());
}
ConnectionInfo bsConnectionInfo = new ConnectionInfo(thriftHost, thriftPort, ByteBuffer.wrap("Just array".getBytes()));
BootstrapNodeInfo bsNode = getBootstrapNodeInfo(bsConnectionInfo);
dm.onNodeUpdated(bsNode);
dm.recalculate();
checkBSNode();
}
/**
* Test Bootstrap Node remove
*/
@Test
public void bootstrapNodeDeleteTest() {
LOG.info("BootstrapNodeUpdateTest started");
DynamicLoadManager dm = getDynamicLoadManager();
ConnectionInfo bsErrConnectionInfo = new ConnectionInfo(thriftHost, thriftPort + 1, ByteBuffer.wrap("Just array".getBytes()));
BootstrapNodeInfo bsErrNode = getBootstrapNodeInfo(bsErrConnectionInfo);
dm.onNodeAdded(bsErrNode);
ConnectionInfo bsConnectionInfo = new ConnectionInfo(thriftHost, thriftPort, ByteBuffer.wrap("Just array".getBytes()));
BootstrapNodeInfo bsNode = getBootstrapNodeInfo(bsConnectionInfo);
dm.onNodeAdded(bsNode);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
fail(e.toString());
}
dm.onNodeRemoved(bsErrNode);
dm.recalculate();
checkBSNode();
}
/**
* Test Operations Node Update Update with two phases, one with same
* ConnectionInfo DNS Name, second with changed ConnectionInfo DNS Name
*/
@Test
public void operationsNodeUpdateTest() {
LOG.info("BootstrapNodeUpdateTest started");
DynamicLoadManager dm = getDynamicLoadManager();
OperationsNodeInfo nodeInfo = generateOperationsNodeInfo("localhost", 1200, 9898, ByteBuffer.wrap("Just array modified".getBytes()), 1);
dm.onNodeUpdated(nodeInfo);
LOG.info("BootstrapNodeTest Operations Node {} updated.", nodeInfo.toString());
OperationsNodeInfo nodeInfo2 = generateOperationsNodeInfo("localhost", 1201, 9899,
ByteBuffer.wrap("Just array modified".getBytes()), 1);
dm.onNodeUpdated(nodeInfo2);
LOG.info("BootstrapNodeTest Operations Node {} updated.", nodeInfo.toString());
ConnectionInfo bsConnectionInfo = new ConnectionInfo(thriftHost, thriftPort, ByteBuffer.wrap("Just array".getBytes()));
BootstrapNodeInfo bsNode = getBootstrapNodeInfo(bsConnectionInfo);
dm.onNodeAdded(bsNode);
assertNotNull(bootstrapThrift.getBootstrapThriftServiceImpl());
assertNotNull(bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap());
assertEquals(2, bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap().size());
assertNotNull(bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap().get("localhost:1200"));
assertNotNull(bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap().get("localhost:1201"));
assertEquals((long) 10, (long) bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap().get("localhost:1200")
.getPriority());
assertEquals((long) 10, (long) bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap().get("localhost:1201")
.getPriority());
}
/**
* Test Operations Node Remove
*/
@Test
public void operationsNodeRemoveTest() {
LOG.info("BootstrapNodeRemoveTest started");
bootstrapThrift.getBootstrapThriftServiceImpl().reset();
DynamicLoadManager dm = getDynamicLoadManager();
ConnectionInfo bsConnectionInfo = new ConnectionInfo(thriftHost, thriftPort, ByteBuffer.wrap("Just array".getBytes()));
BootstrapNodeInfo bsNode = getBootstrapNodeInfo(bsConnectionInfo);
dm.onNodeAdded(bsNode);
checkBSNode();
OperationsNodeInfo nodeInfo = generateOperationsNodeInfo("localhost", 1201, 9899, ByteBuffer.wrap("Just".getBytes()), 1);
bootstrapThrift.getBootstrapThriftServiceImpl().reset();
dm.onNodeAdded(nodeInfo);
assertNotNull(bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap());
assertEquals(2, bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap().size());
bootstrapThrift.getBootstrapThriftServiceImpl().reset();
dm.onNodeRemoved(nodeInfo);
checkBSNode();
}
@Ignore("TODO: FIX before merge to master")
@Test
public void sendRedirectionRuleTest() {
LOG.info("BootstrapNodeUpdateTest started");
DynamicLoadManager dm = getDynamicLoadManager();
long timeStarted = System.currentTimeMillis();
OperationsNodeInfo nodeInfo1 = getUpdatedOperationsNode(9898, 10, timeStarted);
OperationsNodeInfo nodeInfo2 = getUpdatedOperationsNode(9899, 0, timeStarted);
LOG.info("BootstrapNodeTest Operations Node {} updated. 1", nodeInfo1.toString());
dm.onNodeUpdated(nodeInfo1);
dm.onNodeUpdated(nodeInfo2);
nodeInfo1 = getUpdatedOperationsNode(9898, 30, timeStarted + 300000);
nodeInfo2 = getUpdatedOperationsNode(9899, 0, timeStarted + 300000);
LOG.info("BootstrapNodeTest Operations Node {} updated. 2", nodeInfo1.toString());
dm.onNodeUpdated(nodeInfo1);
dm.onNodeUpdated(nodeInfo2);
nodeInfo1 = getUpdatedOperationsNode(9898, 55, timeStarted + 600000);
nodeInfo2 = getUpdatedOperationsNode(9899, 0, timeStarted + 600000);
LOG.info("BootstrapNodeTest Operations Node {} updated. 3", nodeInfo1.toString());
dm.onNodeUpdated(nodeInfo1);
dm.onNodeUpdated(nodeInfo2);
ConnectionInfo bsConnectionInfo = new ConnectionInfo(thriftHost, thriftPort, ByteBuffer.wrap("Just array".getBytes()));
BootstrapNodeInfo bsNode = getBootstrapNodeInfo(bsConnectionInfo);
dm.onNodeAdded(bsNode);
assertNotNull(bootstrapThrift.getBootstrapThriftServiceImpl());
assertNotNull(bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap());
assertEquals(2, bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap().size());
bootstrapThrift.getBootstrapThriftServiceImpl().reset();
dm.recalculate();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
fail(e.toString());
}
}
private OperationsNodeInfo getUpdatedOperationsNode(int httpPort, int processRequestCount, long timeStart) {
Integer processedRequestCount = new Integer(processRequestCount);
OperationsNodeInfo nodeInfo = generateOperationsNodeInfo("localhost", 1200, httpPort, ByteBuffer.wrap("Just array".getBytes()),
processedRequestCount);
return nodeInfo;
}
private DynamicLoadManager getDynamicLoadManager() {
DynamicLoadManager dm = new DynamicLoadManager(ldServiceMock);
assertNotNull(dm);
Integer processedRequestCount = new Integer(0);
OperationsNodeInfo nodeInfo = generateOperationsNodeInfo("localhost", 1200, 9898, ByteBuffer.wrap("Just array".getBytes()),
processedRequestCount);
dm.onNodeAdded(nodeInfo);
LOG.info("BootstrapNodeTest Operations Node {} added.", nodeInfo.toString());
return dm;
}
private void checkBSNode() {
assertNotNull(bootstrapThrift.getBootstrapThriftServiceImpl());
assertNotNull(bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap());
assertNotNull(bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap().get("localhost:1200"));
assertEquals(DEFAULT_PRIORITY, bootstrapThrift.getBootstrapThriftServiceImpl().getOperatonsServerMap().get("localhost:1200")
.getPriority());
}
private OperationsNodeInfo generateOperationsNodeInfo(String thriftHost, int thriftPort, int httpPort, ByteBuffer publicKey,
int loadInfo) {
OperationsNodeInfo nodeInfo = new OperationsNodeInfo();
nodeInfo.setTimeStarted(System.currentTimeMillis());
nodeInfo.setTransports(new ArrayList<TransportMetaData>());
nodeInfo.setLoadInfo(new LoadInfo(loadInfo, 1.0));
nodeInfo.setConnectionInfo(new ConnectionInfo(thriftHost, thriftPort, publicKey));
return nodeInfo;
}
private BootstrapNodeInfo getBootstrapNodeInfo(ConnectionInfo bsConnectionInfo) {
BootstrapNodeInfo nodeInfo = new BootstrapNodeInfo();
nodeInfo.setConnectionInfo(bsConnectionInfo);
nodeInfo.setTimeStarted(System.currentTimeMillis());
nodeInfo.setTransports(new ArrayList<TransportMetaData>());
return nodeInfo;
}
/**
* ThriftRunner Class. Used to run thrift servers.
*/
public class ThriftRunner implements Runnable {
private final String thriftHost;
private final int thriftPort;
private final BootstrapThriftServiceImpl bootstrapThriftService;
private final Object stopSync;
private final Object startSync;
private boolean stopComplete = false;
private boolean startComplete = false;
/**
* The server.
*/
private TServer server;
public ThriftRunner(String thriftHost, int thriftPort) {
this.thriftHost = thriftHost;
this.thriftPort = thriftPort;
this.stopSync = new Object();
this.startSync = new Object();
bootstrapThriftService = new BootstrapThriftServiceImpl();
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
LOG.info("Initializing Thrift Service for Bootstrap Server....");
LOG.info("thrift host: {}", thriftHost);
LOG.info("thrift port: {}", thriftPort);
try {
TMultiplexedProcessor processor = new TMultiplexedProcessor();
BootstrapThriftService.Processor<BootstrapThriftService.Iface> bootstrapProcessor = new BootstrapThriftService.Processor<BootstrapThriftService.Iface>(
bootstrapThriftService);
processor.registerProcessor(KaaThriftService.BOOTSTRAP_SERVICE.getServiceName(), bootstrapProcessor);
TServerTransport serverTransport = new TServerSocket(new InetSocketAddress(thriftHost, thriftPort));
server = new TThreadPoolServer(new Args(serverTransport).processor(processor));
LOG.info("Bootstrap test Server {}:{} Started.", thriftHost, thriftPort);
synchronized (startSync) {
startComplete = true;
startSync.notify();
}
server.serve();
LOG.info("Bootstrap test Server {}:{} Stopped.", thriftHost, thriftPort);
} catch (TTransportException e) {
LOG.error("TTransportException", e);
} finally {
synchronized (stopSync) {
stopComplete = true;
bootstrapThriftService.reset();
stopSync.notify();
}
}
}
public void waitStart() {
LOG.info("Bootstrap test Server {}:{} waitStart()", thriftHost, thriftPort);
synchronized (startSync) {
if (!startComplete) {
try {
startSync.wait(60000);
} catch (InterruptedException e) {
LOG.error("Interupted ThiftRunner startWait()", e);
}
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
LOG.error("Interupted ThiftRunner startWait() in sleep", e);
}
}
public void shutdown() {
LOG.info("Bootstrap test Server {}:{} shutdown()", thriftHost, thriftPort);
server.stop();
synchronized (stopSync) {
if (!stopComplete) {
try {
stopSync.wait(60000);
} catch (InterruptedException e) {
LOG.error("Interupted ThiftRunner shutdown", e);
}
}
}
}
public BootstrapThriftServiceImpl getBootstrapThriftServiceImpl() {
return bootstrapThriftService;
}
}
}