| /* |
| * 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'; |
| } |
| } |
| |