Return-Path:
X-Original-To: apmail-ambari-commits-archive@www.apache.org
Delivered-To: apmail-ambari-commits-archive@www.apache.org
Received: from mail.apache.org (hermes.apache.org [140.211.11.3])
by minotaur.apache.org (Postfix) with SMTP id C673517C8B
for ;
Wed, 14 Jan 2015 20:36:01 +0000 (UTC)
Received: (qmail 80150 invoked by uid 500); 14 Jan 2015 20:36:03 -0000
Delivered-To: apmail-ambari-commits-archive@ambari.apache.org
Received: (qmail 80119 invoked by uid 500); 14 Jan 2015 20:36:03 -0000
Mailing-List: contact commits-help@ambari.apache.org; run by ezmlm
Precedence: bulk
List-Help:
List-Unsubscribe:
List-Post:
List-Id:
Reply-To: ambari-dev@ambari.apache.org
Delivered-To: mailing list commits@ambari.apache.org
Received: (qmail 80109 invoked by uid 99); 14 Jan 2015 20:36:03 -0000
Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org)
(140.211.11.114)
by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 14 Jan 2015 20:36:03 +0000
Received: by tyr.zones.apache.org (Postfix, from userid 65534)
id 29002A42B15; Wed, 14 Jan 2015 20:36:03 +0000 (UTC)
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
From: jaimin@apache.org
To: commits@ambari.apache.org
Message-Id: <1eaed6e4a7a649e08e868dc09ab8d476@git.apache.org>
X-Mailer: ASF-Git Admin Mailer
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 +0000 (UTC)
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
Authored: Wed Jan 14 12:35:46 2015 -0800
Committer: Jaimin Jetly
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.
+ *
+ * It has two potential clients.
+ *
+ * - Ambari Agent:
+ * Uses it to make sure host can talk to specified KDC Server
+ *
+ *
+ * - Ambari Server:
+ * Uses it for connection check, like agent, and also validates
+ * the credentials provided on Server side.
+ *
+ *
+ *
+ */
+@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("")
+ .append("").append(status).append("")
+ .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("\n")
+ .append("").append("Status").append("\n")
+ .append("").append(status).append("
\n")
+ .append(" ")
+ .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");
+ }
+ }
+}