blob: c8af4054238bb702b3ff39b1e9c42f188cf4d8ad [file] [log] [blame]
/*
* 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.
*/
/*-------------------------------------------------------------------------
*
* gpcheckhdfs.c
*
* This file mainly provide functionalities to check status of
* HDFS cluster when:
* 1. doing HAWQ cluster initialization;
* 2. monitoring HDFS cluster health manually.
*
* It uses libhdfs to communicate with HDFS.
*
*-------------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include "krb5/krb5.h"
#include "hdfs/hdfs.h"
/*
* Check Error Code
*/
#define GPCHKHDFS_ERR 1
#define CONNECT_ERR 100
#define OPENFILE_ERR 101
#define WRITEFILE_ERR 102
#define FLUSH_ERR 103
#define DELETE_ERR 104
#define DFSDIR_ERR 105
#define GETTOKEN_ERR 106
#define KRBLOGIN_ERR 107
#define DIRECTORY_ERR 108
#define TRASH_DIRECTORY_NAME ".Trash"
char * conncat(const char * dfs_name, const char * dfs_url);
void getHostAndPort(const char * dfs_url, char * host, char * port);
/*
* test whether hdfs can be connected while kerberos is on or not
*/
int testHdfsConnect(hdfsFS * fs, const char * host, int port,
const char * krbstatus, const char * krb_srvname,
const char * krb_keytabfile);
/*
* test whether the filepath which dfs_url defined in hdfs is existed or not.
* Note: if tde_keyname is not NULL, create an encryption zone on filepath by this key.
*/
int testHdfsExisted(hdfsFS fs, const char * filepath, const char * dfscompleteurl, const char * tde_keyname);
/*
* test whether basic file operation in hdfs is worked well or not
*/
int testHdfsOperateFile(hdfsFS fs, const char * filepath, const char * dfscompleteurl);
int main(int argc, char * argv[]) {
/*
* argv will read from conf
* argv[1]:dfs_name
* argv[2]:dfs_url
* argv[3]:krb status
* argv[4]:krb service name
* argv[5]:krb keytab file
* argv[6]:--with-tde
* argv[7]:tde encryption zone key name
*/
if (argc < 3 || argc > 8
|| ((argc == 4 || argc == 5) && 0 != strcasecmp(argv[3], "off") && 0 != strcasecmp(argv[3], "false"))) {
fprintf(stderr,
"ERROR: gpcheckhdfs parameter error, Please check your config file\n"
"\tDFS_NAME and DFS_URL are required, KERBEROS_SERVICENAME, KERBEROS_KEYFILE "
"ENABLE_SECURE_FILESYSTEM and TDE_KeyName are optional\n");
return GPCHKHDFS_ERR;
}
char * dfs_name = argv[1];
char * dfs_url = argv[2];
char * krbstatus = NULL;
char * krb_srvname = NULL;
char * krb_keytabfile = NULL;
char * tde_keyname = NULL;
if (argc >= 4) {
krbstatus = argv[3];
}
if (argc >= 6) {
krb_srvname = argv[4];
krb_keytabfile = argv[5];
}
//get tde key name param
//to avoid the empty param's influences before it, use loop find
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--with-tde") == 0 && i+1 < argc)
tde_keyname = argv[i+1];
}
char * host = (char *)malloc(255 * sizeof(char));
char * port = (char *)malloc(5 * sizeof(char));
getHostAndPort(dfs_url, host, port);
int iPort = atoi(port);
if (iPort < 0) {
fprintf(stderr, "ERROR: Invalid NameNode Port, Please Check\n");
return CONNECT_ERR;
}
hdfsFS fs;
int connErrCode = testHdfsConnect(&fs, host, iPort, krbstatus, krb_srvname,
krb_keytabfile);
if (connErrCode) {
return connErrCode;
}
char * filename = "/testFile";
char * dfscompleteurl = conncat(dfs_name, dfs_url);
char * filepath = strchr(dfs_url, '/');
if (!filepath) {
fprintf(stderr,"ERROR: argv[2] does not contain '/'");
return DIRECTORY_ERR;
}
/*
* check hdfs's directory configured in dfs_url
* such as sdw2:8020/gpsql/,the filepath is /gpsql
* */
int checkdirErrCode = testHdfsExisted(fs, filepath, dfscompleteurl, tde_keyname);
if (checkdirErrCode) {
return checkdirErrCode;
}
// hawq init should not wait until datanode is ready
//strcat(filepath, filename);
//int operateErrCode = testHdfsOperateFile(fs, filepath, dfscompleteurl);
//if (operateErrCode) {
// return operateErrCode;
//}
free(host);
free(port);
return 0;
}
int testHdfsOperateFile(hdfsFS fs, const char * filepath, const char * dfscompleteurl) {
hdfsFile testFile = hdfsOpenFile(fs, filepath, O_CREAT, 0, 0, 0);
if (NULL == testFile) {
fprintf(stderr, "ERROR:'hdfsOpenFile' failed to create file %s\n", dfscompleteurl);
return OPENFILE_ERR;
}
char * testbuff = "Test file....";
int ts = hdfsWrite(fs, testFile, testbuff, strlen(testbuff));
if (ts < 0) {
fprintf(stderr, "ERROR:'hdfsWrite' failed to write to file %s\n", dfscompleteurl);
return WRITEFILE_ERR;
}
int rv = hdfsHFlush(fs, testFile);
if (rv < 0) {
fprintf(stderr, "ERROR:'hdfsHFlush' failed to flush file\n");
return FLUSH_ERR;
}
rv = hdfsCloseFile(fs, testFile);
if (rv < 0) {
fprintf(stderr, "ERROR:'hdfsClose' failed to close file\n");
return FLUSH_ERR;
}
rv = hdfsDelete(fs, filepath, 0);
if (rv < 0) {
fprintf(stderr, "ERROR:'hdfsDelete' failed to delete %s\n", dfscompleteurl);
return DELETE_ERR;
}
return 0;
}
int testHdfsConnect(hdfsFS * fsptr, const char * host, int iPort,
const char * krbstatus, const char * krb_srvname,
const char * krb_keytabfile) {
struct hdfsBuilder * builder = hdfsNewBuilder();
hdfsBuilderSetNameNode(builder, host);
if (iPort > 0)
hdfsBuilderSetNameNodePort(builder, iPort);
if (NULL != krbstatus && NULL != krb_keytabfile &&
(!strcasecmp(krbstatus, "on") || !strcasecmp(krbstatus, "true"))) { //Kerberos if On
char * krb5_ccname = "/tmp/postgres.ccname";
char cmd[1024];
snprintf(cmd, sizeof(cmd), "kinit -k -t %s -c %s %s",
krb_keytabfile, krb5_ccname, krb_srvname);
if (system(cmd)) {
fprintf(stderr, "ERROR: Failed to login to Kerberos, command line: '%s'\n", cmd);
return KRBLOGIN_ERR;
}
hdfsBuilderSetKerbTicketCachePath(builder, krb5_ccname);
*fsptr = hdfsBuilderConnect(builder);
if (NULL == (*fsptr)) {
if (iPort > 0)
fprintf(stderr, "ERROR: (None HA) Can not connect to 'hdfs://%s:%d'\n", host, iPort);
else
fprintf(stderr, "ERROR: (HA) Can not connect to 'hdfs://%s'\n", host);
fprintf(stderr, "Please check your HDFS or hdfs-client.xml in ${GPHOME}/etc\n");
return CONNECT_ERR;
}
hdfsFreeBuilder(builder);
char * token = hdfsGetDelegationToken((*fsptr), krb_srvname);
if (NULL == token) {
fprintf(stderr, "ERROR: Get Delegation Token Error\n");
return GETTOKEN_ERR;
}
builder = hdfsNewBuilder();
hdfsBuilderSetNameNode(builder, host);
if (iPort > 0)
hdfsBuilderSetNameNodePort(builder, iPort);
hdfsBuilderSetToken(builder, token);
}
*fsptr = hdfsBuilderConnect(builder);
if (NULL == (*fsptr)) {
fprintf(stderr, "ERROR: Can not connect to 'hdfs://%s:%d'\n", host, iPort);
return CONNECT_ERR;
}
hdfsFreeBuilder(builder);
return 0;
}
/*
* check path is a trash directory
* path, e.g: /hawq_default/.Trash
*/
static int isTrashDirectory(const char *path) {
if (path == NULL)
return 0;
size_t len = strlen(TRASH_DIRECTORY_NAME);
size_t path_len = strlen(path);
if (path_len <= len)
return 0;
return strncmp(path+path_len-len, TRASH_DIRECTORY_NAME, len+1) == 0;
}
int testHdfsExisted(hdfsFS fs, const char * filepath, const char * dfscompleteurl, const char * tde_keyname) {
int notExisted = hdfsExists(fs, filepath);
if (notExisted) {
fprintf(stderr, "WARNING:'%s' does not exist, create it ...\n", dfscompleteurl);
int rv = hdfsCreateDirectory(fs, filepath);
if (rv < 0) {
fprintf(stderr, "ERROR: failed to create directory %s\n", dfscompleteurl);
return DFSDIR_ERR;
}
} else {
int num = 0;
hdfsFileInfo * fi = hdfsListDirectory(fs, filepath, &num);
if (NULL == fi || num > 1 ||
(num == 1 && !isTrashDirectory(fi[0].mName)) /* skip Trash directory */
)
{
fprintf(stderr, "ERROR: failed to list directory %s or it is not empty\n"
"Please Check your filepath before doing HAWQ cluster initialization.\n", dfscompleteurl);
return DFSDIR_ERR;
}
}
if (tde_keyname != NULL && strlen(tde_keyname) > 0) {
int ret = hdfsCreateEncryptionZone(fs, filepath, tde_keyname);
if (ret != 0) {
fprintf(stderr, "ERROR: create encryption zone on directory %s failed, key_name:%s.\n",
dfscompleteurl, tde_keyname);
return DFSDIR_ERR;
}
}
return 0;
}
char * conncat(const char * dfs_name, const char * dfs_url) {
const char * colon = "://";
int strlength = strlen(dfs_name) + strlen(dfs_url) + strlen(colon) + 1;
char * dfsurl = (char *)malloc(strlength * sizeof(char));
strcpy(dfsurl, dfs_name);
strcat(dfsurl, colon);
strcat(dfsurl, dfs_url);
return dfsurl;
}
void getHostAndPort(const char * dfs_url, char * host, char * port) {
char * colonPos = strchr(dfs_url , ':');
char * spritPos = NULL;
if (colonPos != NULL) { //None HA url case
int hostlength = colonPos - dfs_url;
strncpy(host , dfs_url , hostlength);
*(host + hostlength) = '\0';
char * remainPtr = colonPos + 1;
spritPos = strchr(remainPtr , '/');
int portlength = spritPos - remainPtr;
strncpy(port , remainPtr , portlength);
*(port + portlength) = '\0';
} else { // HA url case
spritPos = strchr(dfs_url , '/');
int nServicelength = spritPos - dfs_url;
strncpy(host, dfs_url, nServicelength);
*(host + nServicelength) = '\0';
*port = '\0';
}
}