001    /*
002     * GangliaContext.java
003     *
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *     http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing, software
015     * distributed under the License is distributed on an "AS IS" BASIS,
016     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017     * See the License for the specific language governing permissions and
018     * limitations under the License.
019     */
020    
021    package org.apache.hadoop.metrics.ganglia;
022    
023    import java.io.IOException;
024    import java.net.DatagramPacket;
025    import java.net.SocketAddress;
026    import java.net.UnknownHostException;
027    
028    import org.apache.commons.logging.Log;
029    import org.apache.commons.logging.LogFactory;
030    import org.apache.hadoop.conf.Configuration;
031    import org.apache.hadoop.metrics.ContextFactory;
032    import org.apache.hadoop.net.DNS;
033    
034    /**
035     * Context for sending metrics to Ganglia version 3.1.x.
036     * 
037     * 3.1.1 has a slightly different wire portal compared to 3.0.x.
038     */
039    public class GangliaContext31 extends GangliaContext {
040    
041      String hostName = "UNKNOWN.example.com";
042    
043      private static final Log LOG = 
044        LogFactory.getLog("org.apache.hadoop.util.GangliaContext31");
045    
046      public void init(String contextName, ContextFactory factory) {
047        super.init(contextName, factory);
048    
049        LOG.debug("Initializing the GangliaContext31 for Ganglia 3.1 metrics.");
050    
051        // Take the hostname from the DNS class.
052    
053        Configuration conf = new Configuration();
054    
055        if (conf.get("slave.host.name") != null) {
056          hostName = conf.get("slave.host.name");
057        } else {
058          try {
059            hostName = DNS.getDefaultHost(
060              conf.get("dfs.datanode.dns.interface","default"),
061              conf.get("dfs.datanode.dns.nameserver","default"));
062          } catch (UnknownHostException uhe) {
063            LOG.error(uhe);
064            hostName = "UNKNOWN.example.com";
065          }
066        }
067      }
068    
069      protected void emitMetric(String name, String type,  String value) 
070        throws IOException
071      {
072        if (name == null) {
073          LOG.warn("Metric was emitted with no name.");
074          return;
075        } else if (value == null) {
076          LOG.warn("Metric name " + name +" was emitted with a null value.");
077          return;
078        } else if (type == null) {
079          LOG.warn("Metric name " + name + ", value " + value + " has no type.");
080          return;
081        }
082    
083        LOG.debug("Emitting metric " + name + ", type " + type + ", value " + 
084          value + " from hostname" + hostName);
085    
086        String units = getUnits(name);
087        if (units == null) {
088          LOG.warn("Metric name " + name + ", value " + value
089            + " had 'null' units");
090          units = "";
091        }
092        int slope = getSlope(name);
093        int tmax = getTmax(name);
094        int dmax = getDmax(name);
095        offset = 0;
096        String groupName = name.substring(0,name.lastIndexOf("."));
097    
098        // The following XDR recipe was done through a careful reading of
099        // gm_protocol.x in Ganglia 3.1 and carefully examining the output of
100        // the gmetric utility with strace.
101    
102        // First we send out a metadata message
103        xdr_int(128);         // metric_id = metadata_msg
104        xdr_string(hostName); // hostname
105        xdr_string(name);     // metric name
106        xdr_int(0);           // spoof = False
107        xdr_string(type);     // metric type
108        xdr_string(name);     // metric name
109        xdr_string(units);    // units
110        xdr_int(slope);       // slope
111        xdr_int(tmax);        // tmax, the maximum time between metrics
112        xdr_int(dmax);        // dmax, the maximum data value
113    
114        xdr_int(1);             /*Num of the entries in extra_value field for 
115                                  Ganglia 3.1.x*/
116        xdr_string("GROUP");    /*Group attribute*/
117        xdr_string(groupName);  /*Group value*/
118    
119        for (SocketAddress socketAddress : metricsServers) {
120          DatagramPacket packet =
121            new DatagramPacket(buffer, offset, socketAddress);
122          datagramSocket.send(packet);
123        }
124    
125        // Now we send out a message with the actual value.
126        // Technically, we only need to send out the metadata message once for
127        // each metric, but I don't want to have to record which metrics we did and
128        // did not send.
129        offset = 0;
130        xdr_int(133);         // we are sending a string value
131        xdr_string(hostName); // hostName
132        xdr_string(name);     // metric name
133        xdr_int(0);           // spoof = False
134        xdr_string("%s");     // format field
135        xdr_string(value);    // metric value
136            
137        for (SocketAddress socketAddress : metricsServers) {
138          DatagramPacket packet = 
139            new DatagramPacket(buffer, offset, socketAddress);
140          datagramSocket.send(packet);
141        }
142      }
143    
144    }