ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jai...@apache.org
Subject ambari git commit: AMBARI-8116. Implement custom command for checking connectivity to KDC, via REST API. (Rishi Pidva via jaimin)
Date Wed, 14 Jan 2015 20:36:03 GMT
Repository: ambari
Updated Branches:
  refs/heads/trunk daea86e2e -> e56c82998


AMBARI-8116. Implement custom command for checking connectivity to KDC, via REST API. (Rishi
Pidva via jaimin)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e56c8299
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e56c8299
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e56c8299

Branch: refs/heads/trunk
Commit: e56c82998d5cc537a3360a03309a1122ff119ea6
Parents: daea86e
Author: Jaimin Jetly <jaimin@hortonworks.com>
Authored: Wed Jan 14 12:35:46 2015 -0800
Committer: Jaimin Jetly <jaimin@hortonworks.com>
Committed: Wed Jan 14 12:35:46 2015 -0800

----------------------------------------------------------------------
 .../server/KdcServerConnectionVerification.java | 133 ++++++++++++++++++
 .../api/rest/KdcServerReachabilityCheck.java    | 107 +++++++++++++++
 .../server/configuration/Configuration.java     |  32 +++++
 .../KdcServerConnectionVerificationTest.java    | 135 +++++++++++++++++++
 4 files changed, 407 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/e56c8299/ambari-server/src/main/java/org/apache/ambari/server/KdcServerConnectionVerification.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/KdcServerConnectionVerification.java
b/ambari-server/src/main/java/org/apache/ambari/server/KdcServerConnectionVerification.java
new file mode 100644
index 0000000..8bfbc5f
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/KdcServerConnectionVerification.java
@@ -0,0 +1,133 @@
+/**
+ * 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 org.apache.ambari.server;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * Utility class which checks connection to Kerberos Server.
+ * <p>
+ * It has two potential clients.
+ * <ul>
+ * <li>Ambari Agent: 
+ * 		Uses it to make sure host can talk to specified KDC Server
+ * </li>
+ * 
+ * <li>Ambari Server: 
+ * 		Uses it for connection check, like agent, and also validates
+ * 		the credentials provided on Server side.
+ * </li>
+ * </ul>
+ * </p>
+ */
+@Singleton
+public class KdcServerConnectionVerification {
+
+  private static Logger LOG = LoggerFactory.getLogger(KdcServerConnectionVerification.class);
+
+  private Configuration config;
+  
+  @Inject
+  public KdcServerConnectionVerification(Configuration config) {
+    this.config = config;
+  }
+
+  
+  /**
+   * Given server IP or hostname, checks if server is reachable i.e.
+   * we can make a socket connection to it. Hostname may contain port
+   * number separated by a colon. 
+   * 
+   * @param kdcHost KDC server IP or hostname (with optional port number)
+   * @return true, if server is accepting connection given port; false otherwise.
+   */
+  public boolean isKdcReachable(String kdcHost) {
+    try {
+      if (kdcHost == null || kdcHost.isEmpty()) {
+        throw new IllegalArgumentException("Invalid hostname for KDC server");
+      }
+      String[] kdcDetails = kdcHost.split(":");
+      if (kdcDetails.length == 1)  {
+        return isKdcReachable(kdcDetails[0], parsePort(config.getDefaultKdcPort()));
+      } else {
+        return isKdcReachable(kdcDetails[0], parsePort(kdcDetails[1]));
+      }
+    } catch (Exception e) {
+      LOG.error("Exception while checking KDC reachability: " + e);
+      return false;
+    }
+  }
+  /**
+   * Given server IP or hostname, checks if server is reachable i.e.
+   * we can make a socket connection to it.
+   * 
+   * @param server KDC server IP or hostname
+   * @param port	 KDC port
+   * @return	true, if server is accepting connection given port; false otherwise.
+   */
+  public boolean isKdcReachable(String server, Integer port) {
+    Socket socket = null;
+    try {
+      socket = new Socket();
+      socket.connect(new InetSocketAddress(server, port), config.getKdcConnectionCheckTimeout());
+    } catch (UnknownHostException e) {
+      LOG.error("Unable to resolve Kerberos Server hostname");
+      return false;
+    } catch (IOException e) {
+      LOG.error("Unable to connect to Kerberos Server");
+      return false;
+    } finally {
+      if (socket != null) {
+        try {
+          socket.close();
+        } catch (IOException e) {
+          LOG.debug("Error while closing socket connection to Kerberos Server. Can be ignored.");
+        }
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * Parses port number from given string.
+   * @param port port number string
+   * @throws NumberFormatException if given string cannot be parsed
+   * @throws IllegalArgumentException if given string is null or empty
+   * @return parsed port number
+   */
+  private final int parsePort(String port) {
+    if (StringUtils.isEmpty(port)) {
+      throw new IllegalArgumentException("Port number must be non-empty, non-null positive
integer");
+    }
+    return Integer.parseInt(port);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e56c8299/ambari-server/src/main/java/org/apache/ambari/server/api/rest/KdcServerReachabilityCheck.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/rest/KdcServerReachabilityCheck.java
b/ambari-server/src/main/java/org/apache/ambari/server/api/rest/KdcServerReachabilityCheck.java
new file mode 100644
index 0000000..827b187
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/rest/KdcServerReachabilityCheck.java
@@ -0,0 +1,107 @@
+/**
+ * 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 org.apache.ambari.server.api.rest;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.ambari.server.KdcServerConnectionVerification;
+import org.apache.ambari.server.StaticallyInject;
+
+import com.google.inject.Inject;
+
+/**
+ * Service responsible for kerberos related resource requests.
+ */
+@StaticallyInject
+@Path("/kdc_check/")
+public class KdcServerReachabilityCheck {
+  private static final String REACHABLE = "REACHABLE";
+  private static final String UNREACHABLE = "UNREACHABLE";
+
+  @Inject
+  private static KdcServerConnectionVerification kdcConnectionChecker;
+
+
+
+  /**
+   * Handles: GET /kdc_check/{hostname}
+   * Checks the reachability of given KDC server
+   *
+   * @param headers             http headers
+   * @param ui                  uri info
+   * @param kdcServerHostName   HostName of KDC server. May contain port separate by a colon
(:)
+   *
+   * @return status whether KDC server is reachable or not
+   */
+  @GET
+  @Path("{hostname}")
+  @Produces(MediaType.TEXT_PLAIN)
+  public String plainTextCheck(@Context HttpHeaders headers, @Context UriInfo ui,
+      @PathParam("hostname") String kdcServerHostName) {
+    String status = UNREACHABLE;
+    if (kdcConnectionChecker.isKdcReachable(kdcServerHostName)) {
+      status = REACHABLE;
+    }   	
+    return status;
+  }
+
+
+  // This method is called if XML is request
+  @GET
+  @Path("{hostname}")
+  @Produces(MediaType.TEXT_XML)
+  public String xmlCheck(@Context HttpHeaders headers, @Context UriInfo ui,
+      @PathParam("hostname") String kdcServerHostName) {
+    String status = UNREACHABLE;
+    if (kdcConnectionChecker.isKdcReachable(kdcServerHostName)) {
+      status = REACHABLE;
+    } 
+    return new StringBuilder()
+    .append("<?xml version=\"1.0\"?>")
+    .append("<status>").append(status).append("</status>")
+    .toString();
+  }
+
+  // This method is called if HTML is request
+  @GET
+  @Path("{hostname}")
+  @Produces(MediaType.TEXT_HTML)
+  public String  htmlCheck(@Context HttpHeaders headers, @Context UriInfo ui,
+      @PathParam("hostname") String kdcServerHostName) {
+    String status = UNREACHABLE;
+    if (kdcConnectionChecker.isKdcReachable(kdcServerHostName)) {
+      status = REACHABLE;
+    } 
+    return new StringBuilder()
+    .append("<html>\n")
+    .append("<title>").append("Status").append("</title>\n")
+    .append("<body><h1>").append(status).append("</body></h1>\n")
+    .append("</html> ")
+    .toString();
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e56c8299/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index e5b4cc4..d8b8854 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -254,6 +254,13 @@ public class Configuration {
   public static final String DEF_ARCHIVE_CONTENT_TYPE;
 
   /**
+   * Kerberos related configuration options
+   */
+  public static final String KDC_PORT_KEY = "default.kdcserver.port";
+  public static final String KDC_PORT_KEY_DEFAULT = "88";
+  public static final String KDC_CONNECTION_CHECK_TIMEOUT_KEY = "kdcserver.connection.check.timeout";
+  public static final String KDC_CONNECTION_CHECK_TIMEOUT_DEFAULT = "10000";
+  /**
    * This key defines whether stages of parallel requests are executed in
    * parallel or sequentally. Only stages from different requests
    * running on not interfering host sets may be executed in parallel.
@@ -432,6 +439,10 @@ public class Configuration {
 
     configsMap.put(SHARED_RESOURCES_DIR_KEY, properties.getProperty(
        SHARED_RESOURCES_DIR_KEY, SHARED_RESOURCES_DIR_DEFAULT));
+    
+    configsMap.put(KDC_PORT_KEY, properties.getProperty(
+        KDC_PORT_KEY, KDC_PORT_KEY_DEFAULT));
+    
 
     File passFile = new File(configsMap.get(SRVR_KSTR_DIR_KEY) + File.separator
         + configsMap.get(SRVR_CRT_PASS_FILE_KEY));
@@ -1253,4 +1264,25 @@ public class Configuration {
   public String getAlertTemplateFile() {
     return properties.getProperty(ALERT_TEMPLATE_FILE);
   }
+  
+  /**
+   * Gets the default KDC port to use when no port is specified in KDC hostname
+   * 
+   * @return the default KDC port to use.
+   */
+  public String getDefaultKdcPort() {
+    return properties.getProperty(KDC_PORT_KEY, KDC_PORT_KEY_DEFAULT);
+  }
+
+  /**
+   * Gets the inactivity timeout value, in milliseconds, for socket connection
+   * made to KDC Server for its reachability verification.
+   * 
+   * @return the timeout value as configured in {@code ambari.properties} 
+   * 				 or {@code 10000 ms} for default.
+   */
+  public int getKdcConnectionCheckTimeout() {
+    return Integer.parseInt(properties.getProperty(
+        KDC_CONNECTION_CHECK_TIMEOUT_KEY, KDC_CONNECTION_CHECK_TIMEOUT_DEFAULT));
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e56c8299/ambari-server/src/test/java/org/apache/ambari/server/api/rest/KdcServerConnectionVerificationTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/api/rest/KdcServerConnectionVerificationTest.java
b/ambari-server/src/test/java/org/apache/ambari/server/api/rest/KdcServerConnectionVerificationTest.java
new file mode 100644
index 0000000..f8ec650
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/api/rest/KdcServerConnectionVerificationTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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 org.apache.ambari.server.api.rest;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.SocketException;
+import java.util.Properties;
+
+import org.apache.ambari.server.KdcServerConnectionVerification;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.springframework.test.annotation.ExpectedException;
+
+/**
+ * Test for {@link KdcServerConnectionVerification}
+ */
+public class KdcServerConnectionVerificationTest  {
+
+  private static Log LOG = LogFactory.getLog(KdcServerConnectionVerificationTest.class);
+
+  private KdcServerConnectionVerification kdcConnectionVerifier;
+  private Properties configProps;
+  private Configuration configuration;
+
+  private static ServerSocket serverSocket = null;
+  private static boolean serverStop = false;
+
+  private static final int KDC_TEST_PORT = 8090;
+  // Some dummy port to test a non-listening KDC server
+  private static final int DUMMY_KDC_PORT = 11234;
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    createSocketServer(KDC_TEST_PORT);
+  }
+
+  @AfterClass
+  public static void afterClass() throws Exception {
+    closeServerSocket();
+  }
+  
+  @Before
+  public void before() throws Exception {
+    configProps = new Properties();
+    configProps.setProperty(Configuration.KDC_PORT_KEY, Integer.toString(KDC_TEST_PORT));
+    configuration = new Configuration(configProps);
+    kdcConnectionVerifier = new KdcServerConnectionVerification(configuration);     
+  }
+
+  @Test
+  public void testWithPortSuccess() throws Exception {
+    assertTrue(kdcConnectionVerifier.isKdcReachable(String.format("localhost:%d", KDC_TEST_PORT)));
+  }
+
+  @Test
+  public void testWithoutPortSuccess() throws Exception {
+    assertTrue(kdcConnectionVerifier.isKdcReachable("localhost"));
+  }
+
+  @Test
+  public void testWithoutPortFailure() throws Exception {
+    // Assumption: test machine has no KDC so nothing listening on port DUMMY_KDC_PORT
+    configProps.setProperty(Configuration.KDC_PORT_KEY, Integer.toString(DUMMY_KDC_PORT));
+    assertFalse(kdcConnectionVerifier.isKdcReachable("localhost"));
+  }
+
+  @Test
+  public void testWithPortFailure() throws Exception {
+    assertFalse(kdcConnectionVerifier.isKdcReachable("localhost:8091"));
+  }
+
+
+  @Test
+  @ExpectedException(NumberFormatException.class)
+  public void testPortParsingFailure() throws Exception {
+    assertFalse(kdcConnectionVerifier.isKdcReachable("localhost:abc"));
+  }
+
+  /**
+   * Socket server for test
+   * We need a separate thread as accept() is a blocking call
+   */
+  private static class SocketThread extends Thread {
+    public void run() {
+      while (serverSocket != null && !serverStop) {
+        try {
+          serverSocket.accept();
+        } catch (SocketException se) {
+          LOG.debug("SocketException during tearDown. Can be safely ignored");
+        } catch (IOException e) {
+          LOG.error("Unexpected exception while accepting connection request");
+        }
+      }
+
+    }
+  }
+
+  private static void createSocketServer(int port) throws Exception {
+    serverSocket = new ServerSocket(port);
+    new SocketThread().start();
+  }
+
+  private static void closeServerSocket() throws Exception {
+    serverStop = true;
+    try{
+      serverSocket.close();
+    } catch (IOException ioe) {
+      LOG.debug("IOException during tearDown. Can be safely ignored");
+    }
+  }
+}


Mime
View raw message