/** * Licensed to Cloudera, Inc. under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. Cloudera, Inc. licenses this file * to you 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. */ /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 com.cloudera.flume.reporter.ganglia; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.junit.Ignore; import org.junit.Test; import com.cloudera.flume.conf.Context; import com.cloudera.flume.conf.FlumeConfiguration; import com.cloudera.flume.conf.FlumeSpecException; import com.cloudera.flume.core.Attributes; import com.cloudera.flume.core.CompositeSink; import com.cloudera.flume.core.Event; import com.cloudera.flume.core.EventImpl; import com.cloudera.flume.core.EventSink; import com.cloudera.flume.core.FanOutSink; import com.cloudera.flume.core.Attributes.Type; import com.cloudera.util.Clock; import com.cloudera.util.NetUtils; /** * This is a test of the quick and dirty ganglia integration. */ public class TestGangliaSink { private static final Logger LOG = Logger.getLogger(TestGangliaSink.class); // TODO (jon) Parens are invalid in metric names. (what other chars are // illegal in XDR Strings? String ATTR_LONG = "long test metric"; String ATTR_INT = "int test metric"; String ATTR_STRING = "string test metric"; String ATTR_DOUBLE = "double test metric"; /** * This test doesn't have a check, instead just it sends bogus data to an * actual ganglia gmond. This needs a human needs to verify. */ @Ignore("Slow test, requires human to verify that values show up in ganglia") @Test public void sendDatatypesToGanglia_3_1_x() throws IOException, InterruptedException { // assumes there is a ganglia on local host // since using udp doesn't matter if it gets there or not. // default ganglia gmond multicast destination ip. String svrs = "239.2.11.71"; EventSink lsnk = new GangliaSink(svrs, ATTR_LONG, "bytes", Type.LONG); EventSink isnk = new GangliaSink(svrs, ATTR_INT, "bytes", Type.INT); EventSink dsnk = new GangliaSink(svrs, ATTR_DOUBLE, "bytes", Type.DOUBLE); EventSink snk = new FanOutSink<EventSink>(lsnk, isnk, dsnk); snk.open(); // This is enough for the data to register. for (int i = 0; i < 60; i++) { Event e = new EventImpl("".getBytes()); Attributes.setLong(e, ATTR_LONG, i * 1000000); Attributes.setInt(e, ATTR_INT, (int) (i * 1000000)); Attributes.setDouble(e, ATTR_DOUBLE, (double) (1.0 / (i % 20))); snk.append(e); Clock.sleep(1000); } snk.close(); } @Test public void testBuilder() throws IOException { EventSink snk = GangliaSink.builder().build(new Context(), "localhost", "foo", "int"); for (int i = 0; i < 10; i++) { snk.open(); snk.append(new EventImpl("".getBytes())); snk.close(); } EventSink snk4 = GangliaSink.builder().build(new Context(), "localhost", "foo", "int", FlumeConfiguration.get().getGangliaServers()); for (int i = 0; i < 10; i++) { snk4.open(); snk4.append(new EventImpl("".getBytes())); snk4.close(); } try { GangliaSink.builder().build(new Context(), "localhost", "foo", "bar"); } catch (IllegalArgumentException e) { // expected a bad type ; return; } fail("expected failure"); } @Test public void testFactoryBuild() throws FlumeSpecException, IOException { EventSink snk = new CompositeSink(new Context(), "ganglia(\"localhost\", \"foo\", \"int\")"); for (int i = 0; i < 10; i++) { snk.open(); snk.append(new EventImpl("".getBytes())); snk.close(); } } // ////////////////////////////////////////////////////////////////// // This is ganglia >= 3.1.x wire format. Basically ripped out of // HADOOP-4675 /** * This class is a runnable that will listen for Ganglia connections. */ class GangliaSocketListener implements Runnable { private boolean isConfigured = false; private boolean hasData = false; private byte[] byteData; private int port; public CountDownLatch listening = new CountDownLatch(1); public CountDownLatch received = new CountDownLatch(1); public CountDownLatch done = new CountDownLatch(1); public void run() { DatagramSocket s; try { s = new DatagramSocket(); setPort(s.getLocalPort()); setConfigured(true); } catch (IOException e) { LOG.warn(e); // release all the latches listening.countDown(); received.countDown(); done.countDown(); return; } listening.countDown(); byte[] b = new byte[8192]; DatagramPacket info = new DatagramPacket(b, b.length); try { s.receive(info); received.countDown(); } catch (IOException e) { LOG.warn(e); return; } LOG.info("Got a new packet, length " + info.getLength()); int bytesRead = info.getLength(); if (bytesRead > 0) setHasData(true); byteData = new byte[info.getLength()]; System.arraycopy(info.getData(), 0, byteData, 0, bytesRead); done.countDown(); } public void setConfigured(boolean isConfigured) { this.isConfigured = isConfigured; } public boolean getConfigured() { return isConfigured; } public void setHasData(boolean hasData) { this.hasData = hasData; } public boolean getHasData() { return hasData; } public byte[] getBytes() { return byteData; } public void setPort(int port) { this.port = port; } public int getPort() { return port; } } /** * This test was originally stolen and hacked from hadoop's * TestGangliaContext31. It has been modified to use latches as * synchronization mechanism -- the previous implementation's synchronization * mechanisms were unreliable. */ @Test public void testGanglia31Metrics() throws IOException, InterruptedException { String hostName = NetUtils.localhost(); GangliaSocketListener listener = new GangliaSocketListener(); Thread listenerThread = new Thread(listener); listenerThread.start(); assertTrue("Took too long to bind to a port", listener.listening.await(5, TimeUnit.SECONDS)); LOG.info("Listening to port " + listener.getPort()); // setup and send some ganglia data. EventSink ganglia = new GangliaSink(hostName + ":" + listener.getPort(), "foo", "bars", Type.INT); ganglia.open(); Event e = new EventImpl("baz".getBytes()); Attributes.setInt(e, "foo", 1337); ganglia.append(e); ganglia.close(); // did the other thread get the data? assertTrue("Took too long to recieve a packet", listener.received.await(5, TimeUnit.SECONDS)); // and then parsed it? assertTrue("Did not receive proper packet", listener.done.await(5, TimeUnit.SECONDS)); assertTrue("Did not recieve Ganglia data", listener.getHasData()); byte[] hostNameBytes = hostName.getBytes(); byte[] xdrBytes = listener.getBytes(); // Make sure that the received bytes from Ganglia has the correct // hostname for this host boolean hasHostname = false; LOG.info("Checking to make sure that the Ganglia data contains host " + hostName); for (int i = 0; i < xdrBytes.length - hostNameBytes.length; i++) { hasHostname = true; for (int j = 0; j < hostNameBytes.length; j++) { if (xdrBytes[i + j] != hostNameBytes[j]) { hasHostname = false; break; } } if (hasHostname) break; } assertTrue("Did not correctly resolve hostname in Ganglia", hasHostname); } }