From 1c4b7d97be18a01ecb54b4da690c02e08a33b009 Mon Sep 17 00:00:00 2001 From: Sriram Krishnan Date: Fri, 4 Oct 2013 17:14:50 -0700 Subject: [PATCH] Issue #34: code to get host name in the cloud from instance metadata --- build.gradle | 1 + .../netflix/genie/server/util/NetUtil.java | 85 ++++++++++++++----- 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/build.gradle b/build.gradle index 485b945085f..0e03b317528 100644 --- a/build.gradle +++ b/build.gradle @@ -68,6 +68,7 @@ project(':genie-server') { compile 'com.sun.jersey.contribs:jersey-guice:1.9.1' compile 'com.netflix.servo:servo-core:0.4.34' compile 'com.netflix.karyon:karyon-extensions:1.0.22' + compile 'commons-httpclient:commons-httpclient:3.1' runtime 'org.apache.derby:derby:10.2.2.0' runtime 'mysql:mysql-connector-java:5.1.16' diff --git a/genie-server/src/main/java/com/netflix/genie/server/util/NetUtil.java b/genie-server/src/main/java/com/netflix/genie/server/util/NetUtil.java index 16940314a41..4779642408d 100644 --- a/genie-server/src/main/java/com/netflix/genie/server/util/NetUtil.java +++ b/genie-server/src/main/java/com/netflix/genie/server/util/NetUtil.java @@ -18,9 +18,14 @@ package com.netflix.genie.server.util; +import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.methods.GetMethod; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,11 +39,17 @@ */ public final class NetUtil { - private static String publicHostName; - private static String localHostName; + private static String cloudHostName; + private static String dcHostName; private static Logger logger = LoggerFactory.getLogger(NetUtil.class); + // the instance meta-data uri's for public and private host/ip's + private static final String PUBLIC_HOSTNAME_URI = + "http://169.254.169.254/latest/meta-data/public-hostname"; + private static final String LOCAL_IPV4_URI = + "http://169.254.169.254/latest/meta-data/local-ipv4"; + private NetUtil() { // never called } @@ -63,10 +74,10 @@ public static String getArchiveURI(String jobID) { } /** - * Return either the public or local dns name, depending on the datacenter. + * Return either the cloud or dc host name, depending on the datacenter. * If the property netflix.genie.server.host is set, that value will always be returned. - * If the property is not set, then the environment variable EC2_PUBLIC_HOSTNAME will - * be used in the cloud, or InetAddress.getLocalHost() will be used in the DC. + * If the property is not set, then the instance metadata will be used in the cloud, + * or InetAddress.getLocalHost() will be used in the DC. * * @return host name */ @@ -84,9 +95,9 @@ public static String getHostName() throws CloudServiceException { String dc = ConfigurationManager.getConfigInstance().getString( "netflix.datacenter"); if ((dc != null) && dc.equals("cloud")) { - hostName = getPublicHostName(); + hostName = getCloudHostName(); } else { - hostName = getLocalHostName(); + hostName = getDCHostName(); } if ((hostName == null) || (hostName.isEmpty())) { @@ -99,31 +110,67 @@ public static String getHostName() throws CloudServiceException { return hostName; } - private static String getPublicHostName() throws CloudServiceException { + private static String getCloudHostName() throws CloudServiceException { logger.debug("called"); - if ((publicHostName != null) && (!publicHostName.isEmpty())) { - return publicHostName; + if ((cloudHostName != null) && (!cloudHostName.isEmpty())) { + return cloudHostName; + } + + // gets the ec2 public hostname, if available + try { + cloudHostName = httpGet(PUBLIC_HOSTNAME_URI); + } catch (IOException ioe) { + String msg = "Unable to get public hostname from instance metadata"; + logger.error(msg, ioe); + throw new CloudServiceException( + HttpURLConnection.HTTP_INTERNAL_ERROR, msg, ioe); } + if ((cloudHostName == null) || (cloudHostName.isEmpty())) { + try { + cloudHostName = httpGet(LOCAL_IPV4_URI); + } catch (IOException ioe) { + String msg = "Unable to get local IP from instance metadata"; + logger.error(msg, ioe); + throw new CloudServiceException( + HttpURLConnection.HTTP_INTERNAL_ERROR, msg, ioe); + } + } + logger.info("cloudHostName=" + cloudHostName); - // gets the ec2 public hostname - publicHostName = System.getenv("EC2_PUBLIC_HOSTNAME"); - logger.debug("publicHostName=" + publicHostName); + return cloudHostName; + } - return publicHostName; + /** + * Returns the response from an HTTP GET call if it succeeds, null otherwise. + * + * @param uri The URI to execute the HTTP GET on + * @return response from an HTTP GET call if it succeeds, null otherwise + * @throws IOException if there was an error with the HTTP request + */ + private static String httpGet(String uri) throws IOException { + String response = null; + HttpClient client = new HttpClient(); + HttpMethod method = new GetMethod(uri); + client.executeMethod(method); + int status = method.getStatusCode(); + if (status == HttpURLConnection.HTTP_OK) { + response = method.getResponseBodyAsString(); + } + return response; } - private static String getLocalHostName() throws CloudServiceException { + private static String getDCHostName() throws CloudServiceException { logger.debug("called"); - if ((localHostName != null) && (!localHostName.isEmpty())) { - return localHostName; + if ((dcHostName != null) && (!dcHostName.isEmpty())) { + return dcHostName; } try { // gets the local instance hostname InetAddress addr = InetAddress.getLocalHost(); - localHostName = addr.getCanonicalHostName(); + dcHostName = addr.getCanonicalHostName(); } catch (Exception e) { String msg = "Unable to get the hostname"; logger.error(msg, e); @@ -131,6 +178,6 @@ private static String getLocalHostName() throws CloudServiceException { HttpURLConnection.HTTP_INTERNAL_ERROR, msg, e); } - return localHostName; + return dcHostName; } }