diff --git a/cli/gluster-block.c b/cli/gluster-block.c index 2cbb56b..be92eac 100644 --- a/cli/gluster-block.c +++ b/cli/gluster-block.c @@ -26,6 +26,8 @@ "[force] [--json*]" # define GB_REPLACE_HELP_STR "gluster-block replace " \ " [force] [--json*]" +# define GB_GENCONF_HELP_STR "gluster-block genconfig "\ + "enable-tpg [--json*]" # define GB_INFO_HELP_STR "gluster-block info [--json*]" # define GB_LIST_HELP_STR "gluster-block list [--json*]" @@ -49,7 +51,8 @@ typedef enum clioperations { DELETE_CLI = 4, MODIFY_CLI = 5, MODIFY_SIZE_CLI = 6, - REPLACE_CLI = 7 + REPLACE_CLI = 7, + GENCONF_CLI = 8 } clioperations; @@ -67,6 +70,7 @@ glusterBlockCliRPC_1(void *cobj, clioperations opt) blockModifyCli *modify_obj; blockModifySizeCli *modify_size_obj; blockReplaceCli *replace_obj; + blockGenConfigCli *genconfig_obj; blockResponse reply = {0,}; char errMsg[2048] = {0}; @@ -170,6 +174,14 @@ glusterBlockCliRPC_1(void *cobj, clioperations opt) goto out; } break; + case GENCONF_CLI: + genconfig_obj = cobj; + if (block_gen_config_cli_1(genconfig_obj, &reply, clnt) != RPC_SUCCESS) { + LOG("cli", GB_LOG_ERROR, "%sgenconfig on volume %s failed", + clnt_sperror(clnt, "block_gen_config_cli_1"), genconfig_obj->volume); + goto out; + } + break; } out: @@ -259,6 +271,30 @@ glusterBlockIsNameAcceptable(char *name) return TRUE; } +static bool +glusterBlockIsVolListAcceptable(char *name) +{ + char *tok, *tmp; + char delim[2] = {'\0', }; + + + if (!name || GB_STRDUP(tmp, name) < 0) + return FALSE; + + delim[0] = GB_VOLS_DELIMITER; + tok = strtok(tmp, delim); + while (tok != NULL) { + if (!glusterBlockIsNameAcceptable(tok)) { + GB_FREE(tmp); + return FALSE; + } + tok = strtok(NULL, delim); + } + + GB_FREE(tmp); + return TRUE; +} + static bool glusterBlockIsAddrAcceptable(char *addr) { @@ -752,6 +788,48 @@ glusterBlockReplace(int argcount, char **options, int json) } +static int +glusterBlockGenConfig(int argcount, char **options, int json) +{ + blockGenConfigCli robj = {0}; + int ret = -1; + char helpMsg[256] = {0, }; + + + GB_ARGCHECK_OR_RETURN(argcount, 5, "genconfig", GB_GENCONF_HELP_STR); + + if (!glusterBlockIsVolListAcceptable(options[2])) { + MSG("volume list(%s) should be delimited by '%c' character only\n%s\n", + options[2], GB_VOLS_DELIMITER, GB_GENCONF_HELP_STR); + goto out; + } + + GB_STRCPYSTATIC(robj.volume, options[2]); + + if (!strcmp(options[3], "enable-tpg")) { + if (!glusterBlockIsAddrAcceptable(options[4])) { + MSG("host addr (%s) should be a valid ip address\n%s\n", + options[3], GB_REPLACE_HELP_STR); + goto out; + } + GB_STRCPYSTATIC(robj.addr, options[4]); + } else { + MSG("unknown option '%s' for genconfig:\n%s\n", options[3], GB_GENCONF_HELP_STR); + return -1; + } + robj.json_resp = json; + + ret = glusterBlockCliRPC_1(&robj, GENCONF_CLI); + if (ret) { + LOG("cli", GB_LOG_ERROR, "failed genconfig on volume %s", robj.volume); + } + + out: + + return ret; +} + + static int glusterBlockParseArgs(int count, char **options) { @@ -813,7 +891,12 @@ glusterBlockParseArgs(int count, char **options) LOG("cli", GB_LOG_ERROR, "%s", FAILED_REPLACE); } goto out; - + case GB_CLI_GENCONFIG: + ret = glusterBlockGenConfig(count, options, json); + if (ret) { + LOG("cli", GB_LOG_ERROR, "%s", FAILED_GENCONFIG); + } + goto out; case GB_CLI_DELETE: ret = glusterBlockDelete(count, options, json); if (ret) { diff --git a/rpc/block_svc_routines.c b/rpc/block_svc_routines.c index 7ec1377..6b8c428 100644 --- a/rpc/block_svc_routines.c +++ b/rpc/block_svc_routines.c @@ -60,7 +60,8 @@ typedef enum operations { REPLACE_GET_PORTAL_TPG_SRV, LIST_SRV, INFO_SRV, - VERSION_SRV + VERSION_SRV, + GENCONFIG_SRV } operations; @@ -99,6 +100,12 @@ typedef struct blockRemoteModifyResp { } blockRemoteModifyResp; +typedef struct soTgObj { + struct json_object *so_arr; + struct json_object *tg_arr; +} soTgObj; + + typedef struct blockRemoteDeleteResp { char *d_attempt; char *d_success; @@ -2488,6 +2495,335 @@ block_replace_cli_1_svc_st(blockReplaceCli *blk, struct svc_req *rqstp) blockRemoteReplaceRespFree(savereply); optfail: + if (lkfd && glfs_close(lkfd) != 0) { + LOG("mgmt", GB_LOG_ERROR, + "glfs_close(%s): on volume %s for block %s failed[%s]", + GB_TXLOCKFILE, blk->volume, blk->block_name, strerror(errno)); + } + + return reply; +} + + +struct json_object * +getTpgObj(char *block, MetaInfo *info, blockGenConfigCli *blk, char *portal, int tag) +{ + int auth = 0; + uuid_t uuid; + char alias[UUID_BUF_SIZE]; + char alias_10[11] = {'\0', }; + char lun_so[256] = {'\0', }; + + struct json_object *tpg_obj = json_object_new_object(); + struct json_object *tpg_attr_obj = json_object_new_object(); + struct json_object *tpg_luns_arr = json_object_new_array(); + struct json_object *tpg_lun_obj = json_object_new_object(); + struct json_object *tpg_portals_arr = json_object_new_array(); + struct json_object *tpg_portal_obj = json_object_new_object(); + struct json_object *tpg_params_obj = json_object_new_object(); + + + // { Tpg Object open + auth = 1; + if(info->passwd[0] == '\0') { + auth = 0; + } + + json_object_object_add(tpg_attr_obj, "authentication", json_object_new_int(auth)); + json_object_object_add(tpg_attr_obj, "cache_dynamic_acls", json_object_new_int(1)); + json_object_object_add(tpg_attr_obj, "demo_mode_write_protect", json_object_new_int(0)); + json_object_object_add(tpg_attr_obj, "generate_node_acls", json_object_new_int(1)); + if (strcmp(blk->addr, portal)) { + json_object_object_add(tpg_attr_obj, "tpg_enabled_sendtargets", json_object_new_int(0)); + } + json_object_object_add(tpg_obj, "attributes", tpg_attr_obj); + if (auth) { + json_object_object_add(tpg_obj, "chap_password", GB_JSON_OBJ_TO_STR(info->passwd)); + json_object_object_add(tpg_obj, "chap_userid", GB_JSON_OBJ_TO_STR(info->gbid)); + } + + if (!strcmp(blk->addr, portal)) { + json_object_object_add(tpg_obj, "enable", json_object_new_boolean(TRUE)); + } else { + json_object_object_add(tpg_obj, "enable", json_object_new_boolean(FALSE)); + } + + // "luns" : [ + uuid_generate(uuid); + uuid_unparse(uuid, alias); + snprintf(alias_10, 11, "%.10s", alias+24); + json_object_object_add(tpg_lun_obj, "alias", GB_JSON_OBJ_TO_STR(alias_10)); + snprintf(lun_so, 256, "/backstores/user/%s", block); + json_object_object_add(tpg_lun_obj, "storage_object", GB_JSON_OBJ_TO_STR(lun_so)); + json_object_object_add(tpg_lun_obj, "index", json_object_new_int(0)); + json_object_array_add(tpg_luns_arr, tpg_lun_obj); + json_object_object_add(tpg_obj, "luns", tpg_luns_arr); + // ] + + if (auth) { + json_object_object_add(tpg_params_obj, "AuthMethod", GB_JSON_OBJ_TO_STR("CHAP")); + } + json_object_object_add(tpg_obj, "parameters", tpg_params_obj); + + // "portals" : [ + json_object_object_add(tpg_portal_obj, "ip_address", GB_JSON_OBJ_TO_STR(portal)); + json_object_object_add(tpg_portal_obj, "port", json_object_new_int(3260)); + json_object_array_add(tpg_portals_arr, tpg_portal_obj); + json_object_object_add(tpg_obj, "portals", tpg_portals_arr); + // ] + + json_object_object_add(tpg_obj, "tag", json_object_new_int(tag)); + // } Tpg Object close + + return tpg_obj; +} + + +struct json_object * +getTgObj(char *block, MetaInfo *info, blockGenConfigCli *blk) +{ + size_t i, tag = 1; + struct json_object *tg_obj = json_object_new_object(); + struct json_object *tpgs_arr = json_object_new_array(); + struct json_object *tpg_obj = NULL; + char iqn[128] = {'\0', }; + + + json_object_object_add(tg_obj, "fabric", GB_JSON_OBJ_TO_STR("iscsi")); + + // "tpgs:" : [ + // { + for (i = 0; i < info->nhosts; i++) { + if (blockhostIsValid (info->list[i]->status)) { + tpg_obj = getTpgObj(block, info, blk, info->list[i]->addr, tag); + json_object_array_add(tpgs_arr, tpg_obj); + tag++; + } + } + // }, + // ] + + json_object_object_add(tg_obj, "tpgs", tpgs_arr); + + snprintf(iqn, 128, "iqn.2016-12.org.gluster-block:%s", info->gbid); + json_object_object_add(tg_obj, "wwn", GB_JSON_OBJ_TO_STR(iqn)); + + return tg_obj; +} + + +struct json_object * +getSoObj(char *block, MetaInfo *info, blockGenConfigCli *blk) +{ + char cfgstr[1024] = {'\0', }; + struct json_object *so_obj = json_object_new_object(); + struct json_object *so_obj_attr = json_object_new_object(); + + + json_object_object_add(so_obj_attr, "cmd_time_out", json_object_new_int(0)); + json_object_object_add(so_obj_attr, "dev_size", json_object_new_int64(info->size)); + + json_object_object_add(so_obj, "attributes", so_obj_attr); + + snprintf(cfgstr, 1024, "glfs/%s@%s/block-store/%s", info->volume, blk->addr, info->gbid); + json_object_object_add(so_obj, "config", GB_JSON_OBJ_TO_STR(cfgstr)); + json_object_object_add(so_obj, "name", GB_JSON_OBJ_TO_STR(block)); + json_object_object_add(so_obj, "plugin", GB_JSON_OBJ_TO_STR("user")); + json_object_object_add(so_obj, "size", json_object_new_int64(info->size)); + json_object_object_add(so_obj, "wwn", GB_JSON_OBJ_TO_STR(info->gbid)); + + return so_obj; +} + + +static int +getSoTgArraysForAllVolume(struct soTgObj *obj, blockGenConfigCli *blk, + char **errMsg, int *errCode) +{ + struct glfs *glfs; + struct glfs_fd *lkfd = NULL; + struct glfs_fd *tgmdfd = NULL; + struct dirent *entry; + MetaInfo *info = NULL; + strToCharArrayDefPtr vols; + size_t i, j; + int ret = -1; + bool partOfBlock; + struct json_object *so_obj = NULL; + struct json_object *tg_obj = NULL; + + + vols = getCharArrayFromDelimitedStr(blk->volume, GB_VOLS_DELIMITER); + if (!vols) { + LOG("mgmt", GB_LOG_ERROR, + "getCharArrayFromDelimitedStr(%s) failed", blk->volume); + goto optfail; + } + + for (i = 0; i < vols->len; i++) { + glfs = glusterBlockVolumeInit(vols->data[i], errCode, errMsg); + if (!glfs) { + LOG("mgmt", GB_LOG_ERROR, + "glusterBlockVolumeInit(%s) failed", vols->data[i]); + goto optfail; + } + + lkfd = glusterBlockCreateMetaLockFile(glfs, vols->data[i], errCode, errMsg); + if (!lkfd) { + LOG("mgmt", GB_LOG_ERROR, "%s %s", FAILED_CREATING_META, vols->data[i]); + goto optfail; + } + + GB_METALOCK_OR_GOTO(lkfd, vols->data[i], *errCode, *errMsg, out); + + tgmdfd = glfs_opendir(glfs, GB_METADIR); + if (!tgmdfd) { + *errCode = errno; + GB_ASPRINTF(errMsg, "Not able to open metadata directory for volume " + "%s[%s]", vols->data[i], strerror(*errCode)); + LOG("mgmt", GB_LOG_ERROR, "glfs_opendir(%s): on volume %s failed[%s]", + GB_METADIR, vols->data[i], strerror(errno)); + ret = -1; + goto out; + } + + while ((entry = glfs_readdir(tgmdfd))) { + if (strcmp(entry->d_name, ".") && + strcmp(entry->d_name, "..") && + strcmp(entry->d_name, GB_TXLOCKFILE) && + strcmp(entry->d_name, GB_PRIO_FILENAME)) { + + if (GB_ALLOC(info) < 0) { + ret = -1; + goto out; + } + ret = blockGetMetaInfo(glfs, entry->d_name, info, NULL); + if (ret) { + goto out; + } + + partOfBlock = false; + for (j = 0; j < info->nhosts; j++) { + if (blockhostIsValid(info->list[j]->status) && !strcmp(info->list[j]->addr, blk->addr)) { + partOfBlock = true; + } + } + if (!partOfBlock) { + blockFreeMetaInfo(info); + continue; + } + + /* storage_objects */ + so_obj = getSoObj(entry->d_name, info, blk); + json_object_array_add(obj->so_arr, so_obj); + + /* targets */ + tg_obj = getTgObj(entry->d_name, info, blk); + json_object_array_add(obj->tg_arr, tg_obj); + + blockFreeMetaInfo(info); + } + } + + GB_METAUNLOCK(lkfd, vols->data[i], *errCode, *errMsg); + if (tgmdfd && glfs_closedir (tgmdfd) != 0) { + LOG("mgmt", GB_LOG_ERROR, "glfs_closedir(%s): on volume %s failed[%s]", + GB_METADIR, vols->data[i], strerror(errno)); + } + if (lkfd && glfs_close(lkfd) != 0) { + LOG("mgmt", GB_LOG_ERROR, "glfs_close(%s): on volume %s failed[%s]", + GB_TXLOCKFILE, vols->data[i], strerror(errno)); + } + } + + ret = 0; + goto free; + + out: + GB_METAUNLOCK(lkfd, vols->data[i], *errCode, *errMsg); + if (tgmdfd && glfs_closedir (tgmdfd) != 0) { + LOG("mgmt", GB_LOG_ERROR, "glfs_closedir(%s): on volume %s failed[%s]", + GB_METADIR, vols->data[i], strerror(errno)); + } + blockFreeMetaInfo(info); + + optfail: + if (lkfd && glfs_close(lkfd) != 0) { + LOG("mgmt", GB_LOG_ERROR, "glfs_close(%s): on volume %s failed[%s]", + GB_TXLOCKFILE, vols->data[i], strerror(errno)); + } + + free: + strToCharArrayDefFree(vols); + + return ret; +} + + +static int +glusterBlockGenConfigSvc(blockGenConfigCli *blk, + blockResponse *reply, char **errMsg, int *errCode) +{ + struct soTgObj *obj = NULL; + struct json_object *jobj = json_object_new_object(); + + + if (GB_ALLOC(obj) < 0) { + goto out; + } + obj->so_arr = json_object_new_array(); + obj->tg_arr = json_object_new_array(); + + reply->exit = getSoTgArraysForAllVolume(obj, blk, errMsg, errCode); + if(reply->exit) { + LOG("mgmt", GB_LOG_ERROR, "getSoTgArraysPerVolume(): on volume[s] %s failed", + blk->volume); + goto out; + } + + json_object_object_add(jobj, "storage_objects", obj->so_arr); + json_object_object_add(jobj, "targets", obj->tg_arr); + + out: + if(reply->exit) { + blockFormatErrorResponse(GENCONFIG_SRV, blk->json_resp, *errCode, + GB_DEFAULT_ERRMSG, reply); + } else { + GB_ASPRINTF(&reply->out, "%s\n", json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PRETTY)); + } + json_object_put(jobj); + GB_FREE(obj); + + return reply->exit; +} + + +blockResponse * +block_gen_config_cli_1_svc_st(blockGenConfigCli *blk, struct svc_req *rqstp) +{ + blockResponse *reply = NULL; + int errCode = 0; + char *errMsg = NULL; + + + LOG("mgmt", GB_LOG_DEBUG, + "genconfig request, volume[s]=%s addr=%s", blk->volume, blk->addr); + + if (GB_ALLOC(reply) < 0) { + return NULL; + } + reply->exit = -1; + + errCode = glusterBlockGenConfigSvc(blk, reply, &errMsg, &errCode); + if (errCode) { + LOG("mgmt", GB_LOG_ERROR, "glusterBlockGenConfigSvc(): on volume[s] %s failed with %s", + blk->volume, errMsg?errMsg:""); + goto out; + } + + LOG("mgmt", GB_LOG_DEBUG, "genconfig cli success, volume[s]=%s", blk->volume); + + out: return reply; } @@ -4994,6 +5330,15 @@ block_replace_cli_1_svc(blockReplaceCli *blk, blockResponse *reply, return ret; } +bool_t +block_gen_config_cli_1_svc(blockGenConfigCli *blk, blockResponse *reply, + struct svc_req *rqstp) +{ + int ret; + + GB_RPC_CALL(gen_config_cli, blk, reply, rqstp, ret); + return ret; +} bool_t block_list_cli_1_svc(blockListCli *blk, blockResponse *reply, diff --git a/rpc/rpcl/block.x b/rpc/rpcl/block.x index c1886d0..199f6e4 100644 --- a/rpc/rpcl/block.x +++ b/rpc/rpcl/block.x @@ -124,6 +124,12 @@ struct blockReplaceCli { enum JsonResponseFormat json_resp; }; +struct blockGenConfigCli { + char volume[255]; + char addr[255]; + enum JsonResponseFormat json_resp; +}; + struct blockResponse { int exit; /* exit code of the command */ string out<>; /* output; TODO: return respective objects */ @@ -153,5 +159,6 @@ program GLUSTER_BLOCK_CLI { blockResponse BLOCK_MODIFY_CLI(blockModifyCli) = 5; blockResponse BLOCK_REPLACE_CLI(blockReplaceCli) = 6; blockResponse BLOCK_MODIFY_SIZE_CLI(blockModifySizeCli) = 7; + blockResponse BLOCK_GEN_CONFIG_CLI(blockGenConfigCli) = 8; } = 1; } = 212153113; /* B2 L12 O15 C3 K11 C3 */ diff --git a/utils/common.c b/utils/common.c index a4cf2f0..e8382d3 100644 --- a/utils/common.c +++ b/utils/common.c @@ -197,6 +197,79 @@ blockServerDefFree(blockServerDefPtr blkServers) } +void +strToCharArrayDefFree(strToCharArrayDefPtr arr) +{ + size_t i; + + + if (!arr) { + return; + } + + for (i = 0; i < arr->len; i++) { + GB_FREE(arr->data[i]); + } + GB_FREE(arr->data); + GB_FREE(arr); +} + + +strToCharArrayDefPtr +getCharArrayFromDelimitedStr(char *str, char delim) +{ + strToCharArrayDefPtr arr; + char *tmp; + char *tok; + char *base; + size_t i = 0; + + if (!str) { + return NULL; + } + + if (GB_STRDUP(tmp, str) < 0) { + return NULL; + } + base = tmp; + + if (GB_ALLOC(arr) < 0) { + goto out; + } + + /* count number of Vols */ + while (*tmp) { + if (*tmp == delim) { + arr->len++; + } + tmp++; + } + arr->len++; + tmp = base; /* reset vols */ + + + if (GB_ALLOC_N(arr->data, arr->len) < 0) { + goto out; + } + + tok = strtok(tmp, &delim); + for (i = 0; tok != NULL; i++) { + if (GB_STRDUP(arr->data[i], tok) < 0) { + goto out; + } + tok = strtok(NULL, &delim); + } + + GB_FREE(base); + return arr; + + out: + GB_FREE(base); + strToCharArrayDefFree(arr); + return NULL; +} + + bool blockhostIsValid(char *status) { diff --git a/utils/common.h b/utils/common.h index 2910229..945378b 100644 --- a/utils/common.h +++ b/utils/common.h @@ -15,6 +15,8 @@ # include "utils.h" # include "block.h" +# define GB_VOLS_DELIMITER ',' + typedef struct blockServerDef { size_t nhosts; @@ -23,6 +25,13 @@ typedef struct blockServerDef { typedef blockServerDef *blockServerDefPtr; +typedef struct strToCharArrayDef { + size_t len; + char **data; +} strToCharArrayDef; +typedef strToCharArrayDef *strToCharArrayDefPtr; + + static const char *const JsonResponseFormatLookup[] = { [GB_JSON_NONE] = "", @@ -81,6 +90,10 @@ static const char *const ConvertStringToTrillianLookup[] = { }; +strToCharArrayDefPtr getCharArrayFromDelimitedStr(char *str, char delim); + +void strToCharArrayDefFree(strToCharArrayDefPtr arr); + enum JsonResponseFormat jsonResponseFormatParse(const char *opt); ssize_t glusterBlockParseSize(const char *dom, char *value); diff --git a/utils/utils.h b/utils/utils.h index fcbca2f..def5977 100644 --- a/utils/utils.h +++ b/utils/utils.h @@ -93,6 +93,9 @@ # define FAILED_REPLACE "failed in replace" # define FAILED_REMOTE_REPLACE "failed in remote replace portal" +/* Config generate */ +# define FAILED_GENCONFIG "failed in generation of config" + # define FAILED_DEPENDENCY "failed dependency, check if you have targetcli and tcmu-runner installed" # define FMT_WARN(fmt...) do { if (0) printf (fmt); } while (0) @@ -350,6 +353,7 @@ typedef enum gbCliCmdlineOption { GB_CLI_DELETE, GB_CLI_MODIFY, GB_CLI_REPLACE, + GB_CLI_GENCONFIG, GB_CLI_HELP, GB_CLI_HYPHEN_HELP, GB_CLI_VERSION, @@ -368,6 +372,7 @@ static const char *const gbCliCmdlineOptLookup[] = { [GB_CLI_DELETE] = "delete", [GB_CLI_MODIFY] = "modify", [GB_CLI_REPLACE] = "replace", + [GB_CLI_GENCONFIG] = "genconfig", [GB_CLI_HELP] = "help", [GB_CLI_HYPHEN_HELP] = "--help", [GB_CLI_VERSION] = "version",