summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPrasanna Kumar Kalever <prasanna.kalever@redhat.com>2018-01-10 23:48:38 +0530
committerPranith Kumar Karampuri <pkarampu@redhat.com>2018-01-18 10:11:39 +0000
commit46f460fa669ce9f7b9f2de9e54cefc3726dcd029 (patch)
tree54a22636bc90649186e4b3001d13f861df75c61f
parent5d98272c53816813872aaa807e4ccc1ee2ad1bfc (diff)
versioning: add capabilities support
Change-Id: Ic63f7c9fa169ee37b796093554c59016fbbfaa46 Signed-off-by: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
-rw-r--r--rpc/block_svc_routines.c472
-rw-r--r--rpc/rpcl/block.x1
-rw-r--r--utils/Makefile.am17
-rw-r--r--utils/capabilities.c109
-rw-r--r--utils/capabilities.h65
-rw-r--r--utils/gluster-block-caps.info98
-rw-r--r--utils/utils.h8
7 files changed, 741 insertions, 29 deletions
diff --git a/rpc/block_svc_routines.c b/rpc/block_svc_routines.c
index c0c437f..433b185 100644
--- a/rpc/block_svc_routines.c
+++ b/rpc/block_svc_routines.c
@@ -10,6 +10,7 @@
# include "common.h"
+# include "capabilities.h"
# include "glfs-operations.h"
# include <pthread.h>
@@ -18,8 +19,8 @@
# include <json-c/json.h>
-# define UUID_BUF_SIZE 38
-# define GB_DEFAULT_ERRCODE 255
+# define UUID_BUF_SIZE 38
+# define GB_DEFAULT_ERRCODE 255
# define GB_CREATE "create"
# define GB_DELETE "delete"
@@ -37,6 +38,8 @@
# define GB_DEFAULT_ERRMSG "Operation failed, please check the log "\
"file to find the reason."
+# define GB_OLD_CAP_MAX 9
+
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
typedef enum operations {
@@ -45,7 +48,8 @@ typedef enum operations {
MODIFY_SRV,
MODIFY_TPGC_SRV,
LIST_SRV,
- INFO_SRV
+ INFO_SRV,
+ VERSION_SRV
} operations;
@@ -170,6 +174,25 @@ mapJsonFlagToJsonCstring(int jsonflag)
}
+static bool
+blockhostIsValid(char *status)
+{
+ switch (blockMetaStatusEnumParse(status)) {
+ case GB_CONFIG_SUCCESS:
+ case GB_CLEANUP_INPROGRESS:
+ case GB_AUTH_ENFORCEING:
+ case GB_AUTH_ENFORCED:
+ case GB_AUTH_ENFORCE_FAIL:
+ case GB_AUTH_CLEAR_ENFORCED:
+ case GB_AUTH_CLEAR_ENFORCEING:
+ case GB_AUTH_CLEAR_ENFORCE_FAIL:
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
static void
blockCreateParsedRespFree(blockRemoteCreateResp *savereply)
{
@@ -299,6 +322,7 @@ blockRemoteCreateRespParse(char *output, blockRemoteCreateResp **savereply)
return -1;
}
+
struct addrinfo *
glusterBlockGetSockaddr(char *host)
{
@@ -373,7 +397,6 @@ out:
}
-
int
glusterBlockCallRPC_1(char *host, void *cobj,
operations opt, bool *rpc_sent, char **out)
@@ -382,8 +405,10 @@ glusterBlockCallRPC_1(char *host, void *cobj,
int ret = -1;
int sockfd = -1;
int errsv = 0;
+ size_t i;
blockResponse reply = {0,};
struct addrinfo *res = NULL;
+ gbCapResp *obj = NULL;
*rpc_sent = FALSE;
@@ -410,6 +435,15 @@ glusterBlockCallRPC_1(char *host, void *cobj,
goto out;
}
break;
+ case VERSION_SRV:
+ *rpc_sent = TRUE;
+ ret = block_version_1((void*)cobj, &reply, clnt);
+ if (ret != RPC_SUCCESS) {
+ LOG("mgmt", GB_LOG_ERROR, "%son host %s",
+ clnt_sperror(clnt, "block remote version failed"), host);
+ goto out;
+ }
+ break;
case DELETE_SRV:
*rpc_sent = TRUE;
if (block_delete_1((blockDelete *)cobj, &reply, clnt) != RPC_SUCCESS) {
@@ -432,10 +466,26 @@ glusterBlockCallRPC_1(char *host, void *cobj,
goto out;
}
- if (GB_STRDUP(*out, reply.out) < 0) {
- goto out;
- }
ret = reply.exit;
+ if (opt != VERSION_SRV) {
+ if (GB_STRDUP(*out, reply.out) < 0) {
+ goto out;
+ }
+ } else {
+ if (GB_ALLOC(obj) < 0) {
+ return -1;
+ }
+ obj->capMax = reply.xdata.xdata_len/sizeof(gbCapObj);
+ gbCapObj *caps = (gbCapObj *)reply.xdata.xdata_val;
+ if (GB_ALLOC_N(obj->response, obj->capMax) < 0) {
+ return -1;
+ }
+ for (i = 0; i < obj->capMax; i++) {
+ strncpy(obj->response[i].cap, caps[i].cap, 256);
+ obj->response[i].status = caps[i].status;
+ }
+ *out = (char *) obj;
+ }
out:
if (clnt) {
@@ -535,6 +585,187 @@ blockServerParse(char *blkServers)
void *
+glusterBlockCapabilitiesRemote(void *data)
+{
+ int ret;
+ blockRemoteObj *args = (blockRemoteObj *)data;
+ bool rpc_sent = FALSE;
+
+
+ /* Get peers capabilities */
+ ret = glusterBlockCallRPC_1(args->addr, NULL, VERSION_SRV, &rpc_sent,
+ &args->reply);
+ if (ret && ret != RPC_PROCUNAVAIL) {
+ if (!rpc_sent) {
+ LOG("mgmt", GB_LOG_ERROR, "%s hence %s on host %s",
+ strerror(errno), FAILED_REMOTE_CAPS, args->addr);
+ ret = -ENOTCONN;
+ } else {
+ LOG("mgmt", GB_LOG_ERROR, "%s for on host %s",
+ FAILED_REMOTE_CAPS, args->addr);
+ ret = -1;
+ }
+ }
+
+ args->exit = ret;
+
+ return NULL;
+}
+
+
+/* function to imitate caps of older gluster-block versions */
+gbCapResp *
+glusterBlockMimicOldCaps(void)
+{
+ size_t i;
+ gbCapResp *caps = NULL;
+
+
+ if (GB_ALLOC(caps) < 0) {
+ return NULL;
+ }
+
+ caps->capMax = GB_OLD_CAP_MAX;
+ if (GB_ALLOC_N(caps->response, GB_OLD_CAP_MAX) < 0) {
+ GB_FREE(caps);
+ return NULL;
+ }
+
+ strncpy(caps->response[0].cap, "create", 256);
+ strncpy(caps->response[1].cap, "create_ha", 256);
+ strncpy(caps->response[2].cap, "create_prealloc", 256);
+ strncpy(caps->response[3].cap, "create_auth", 256);
+
+ strncpy(caps->response[4].cap, "delete", 256);
+ strncpy(caps->response[5].cap, "delete_force", 256);
+
+ strncpy(caps->response[6].cap, "modify", 256);
+ strncpy(caps->response[7].cap, "modify_auth", 256);
+
+ strncpy(caps->response[8].cap, "json", 256);
+
+ for (i = 0; i < GB_OLD_CAP_MAX; i++) {
+ caps->response[i].status = true;
+ }
+
+ return caps;
+}
+
+
+static int
+blockRemoteCapabilitiesRespParse(size_t count, blockRemoteObj *args,
+ bool *minCaps, char **errMsg)
+{
+ size_t i, j, k;
+ int ret = -1;
+ gbCapResp **caps = NULL;
+ bool CAP_MATCH;
+
+
+ if (GB_ALLOC_N(caps, count) < 0) {
+ return -1;
+ }
+ for (i = 0; i < count; i++) {
+ caps[i] = (gbCapResp *) args[i].reply;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (args[i].exit == RPC_PROCUNAVAIL) {
+ caps[i] = glusterBlockMimicOldCaps();
+ } else if (args[i].exit) {
+ ret = args[i].exit;
+ GB_ASPRINTF(errMsg, "host %s returned %d", args[i].addr, ret);
+ goto out;
+ }
+ }
+
+ for (i = 0; i < GB_CAP_MAX; i++) {
+ if (minCaps[i] == false) {
+ continue;
+ }
+ /* Check if all remotes contain this cap */
+ for (j = 0; j < count; j++) {
+ CAP_MATCH=false;
+ if (!caps[j]) {
+ GB_ASPRINTF(errMsg, "capability empty on %s", args[j].addr);
+ goto out;
+ }
+ for (k = 0; k < caps[j]->capMax; k++) {
+ if (!strncmp(gbCapabilitiesLookup[i], caps[j]->response[k].cap, 256) &&
+ caps[j]->response[k].status) {
+ CAP_MATCH=true;
+ break;
+ }
+ }
+ if (!CAP_MATCH) {
+ GB_ASPRINTF(errMsg, "capability '%s' doesn't exit on %s",
+ gbCapabilitiesLookup[i], args[j].addr);
+ goto out;
+ }
+ }
+ }
+
+ ret = 0;
+ out:
+ for (i = 0; i < count; i++) {
+ if (caps[i]) {
+ GB_FREE(caps[i]->response);
+ GB_FREE(caps[i]);
+ }
+ }
+ GB_FREE(caps);
+ return ret;
+}
+
+
+static int
+glusterBlockCapabilityRemoteAsync(blockServerDef *servers, bool *minCaps,
+ char **errMsg)
+{
+ static blockRemoteObj *args = NULL;
+ pthread_t *tid = NULL;
+ int ret = -1;
+ size_t i;
+
+
+ /* skip if nhosts = 1 */
+ if (!servers || (servers->nhosts <= 1)) {
+ return 0;
+ }
+
+ if (GB_ALLOC_N(tid, servers->nhosts) < 0) {
+ goto out;
+ }
+
+ if (GB_ALLOC_N(args, servers->nhosts) < 0) {
+ goto out;
+ }
+
+ for (i = 0; i < servers->nhosts; i++) {
+ args[i].addr = servers->hosts[i];
+ }
+
+ for (i = 0; i < servers->nhosts; i++) {
+ pthread_create(&tid[i], NULL, glusterBlockCapabilitiesRemote, &args[i]);
+ }
+
+ for (i = 0; i < servers->nhosts; i++) {
+ /* collect exit code */
+ pthread_join(tid[i], NULL);
+ }
+
+ /* Verify the capabilities */
+ ret = blockRemoteCapabilitiesRespParse(servers->nhosts, args, minCaps, errMsg);
+
+ out:
+ GB_FREE(args);
+ GB_FREE(tid);
+
+ return ret;
+}
+
+
+void *
glusterBlockCreateRemote(void *data)
{
int ret;
@@ -1360,6 +1591,65 @@ blockStr2arrayAddToJsonObj (json_object *json_obj, char *string, char *label,
}
+bool *
+glusterBlockBuildMinCaps(void *data, operations opt)
+{
+ blockCreateCli *cblk = NULL;
+ blockDeleteCli *dblk = NULL;
+ blockModifyCli *mblk = NULL;
+ bool *minCaps = NULL;
+
+
+ if (GB_ALLOC_N(minCaps, GB_CAP_MAX) < 0) {
+ return NULL;
+ }
+
+ switch (opt) {
+ case CREATE_SRV:
+ cblk = (blockCreateCli *)data;
+
+ minCaps[GB_CREATE_CAP] = true;
+ if (cblk->mpath > 1) {
+ minCaps[GB_CREATE_HA_CAP] = true;
+ }
+ if (cblk->prealloc) {
+ minCaps[GB_CREATE_PREALLOC_CAP] = true;
+ }
+ if (cblk->auth_mode) {
+ minCaps[GB_CREATE_AUTH_CAP] = true;
+ }
+ if (cblk->json_resp) {
+ minCaps[GB_JSON_CAP] = true;
+ }
+ break;
+ case DELETE_SRV:
+ dblk = (blockDeleteCli *)data;
+
+ minCaps[GB_DELETE_CAP] = true;
+ if (dblk->force) {
+ minCaps[GB_DELETE_FORCE_CAP] = true;
+ }
+ if (dblk->json_resp) {
+ minCaps[GB_JSON_CAP] = true;
+ }
+ break;
+ case MODIFY_SRV:
+ mblk = (blockModifyCli *)data;
+
+ minCaps[GB_MODIFY_CAP] = true;
+ if (mblk->auth_mode) {
+ minCaps[GB_MODIFY_AUTH_CAP] = true;
+ }
+ if (mblk->json_resp) {
+ minCaps[GB_JSON_CAP] = true;
+ }
+ break;
+ }
+
+ return minCaps;
+}
+
+
static void
blockModifyCliFormatResponse (blockModifyCli *blk, struct blockModify *mobj,
int errCode, char *errMsg,
@@ -1482,6 +1772,85 @@ blockModifyCliFormatResponse (blockModifyCli *blk, struct blockModify *mobj,
}
+
+static int
+glusterBlockCheckCapabilities(void* blk, operations opt, blockServerDefPtr list,
+ char **errMsg)
+{
+ int errCode = 0;
+ bool *minCaps = NULL;
+ char *localErrMsg = NULL;
+
+
+ /* skip if nhosts = 1 */
+ if (!list || (list->nhosts <= 1)) {
+ return 0;
+ }
+
+ minCaps = glusterBlockBuildMinCaps(blk, opt);
+ if (!minCaps) {
+ errCode = ENOMEM;
+ goto out;
+ }
+
+ errCode = glusterBlockCapabilityRemoteAsync(list, minCaps, &localErrMsg);
+ if (errCode) {
+ LOG("mgmt", GB_LOG_ERROR, "glusterBlockCapabilityRemoteAsync() failed (%s)",
+ localErrMsg);
+ if (errCode == -ENOTCONN) {
+ if (GB_ASPRINTF(errMsg, "Version check failed [%s] (Hint: See if all "
+ "servers are up and running gluster-blockd daemon)",
+ localErrMsg) == -1) {
+ errCode = ENOMEM;
+ }
+ } else {
+ if (GB_ASPRINTF(errMsg, "Version check failed between block servers. (%s)",
+ localErrMsg) == -1) {
+ errCode = ENOMEM;
+ }
+ }
+ goto out;
+ }
+
+ out:
+ GB_FREE(minCaps);
+ GB_FREE(localErrMsg);
+ return errCode;
+}
+
+
+blockServerDefPtr
+glusterBlockGetListFromInfo(MetaInfo *info)
+{
+ size_t i;
+ blockServerDefPtr list = NULL;
+
+
+ if (!info || GB_ALLOC(list) < 0) {
+ return NULL;
+ }
+
+ if (GB_ALLOC_N(list->hosts, info->mpath) < 0) {
+ goto out;
+ }
+
+ for (i = 0; i < info->nhosts; i++) {
+ if (blockhostIsValid (info->list[i]->status)) {
+ if (GB_STRDUP(list->hosts[i], info->list[i]->addr) < 0) {
+ goto out;
+ }
+ list->nhosts++;
+ }
+ }
+
+ return list;
+
+ out:
+ blockServerDefFree(list);
+ return NULL;
+}
+
+
blockResponse *
block_modify_cli_1_svc_st(blockModifyCli *blk, struct svc_req *rqstp)
{
@@ -1498,6 +1867,8 @@ block_modify_cli_1_svc_st(blockModifyCli *blk, struct svc_req *rqstp)
bool rollback = false;
int errCode = 0;
char *errMsg = NULL;
+ blockServerDefPtr list = NULL;
+ size_t i;
LOG("mgmt", GB_LOG_DEBUG,
@@ -1544,6 +1915,20 @@ block_modify_cli_1_svc_st(blockModifyCli *blk, struct svc_req *rqstp)
goto out;
}
+ list = glusterBlockGetListFromInfo(info);
+ if (!list) {
+ errCode = ENOMEM;
+ goto out;
+ }
+
+ errCode = glusterBlockCheckCapabilities((void *)blk, MODIFY_SRV, list, &errMsg);
+ if (errCode) {
+ LOG("mgmt", GB_LOG_ERROR,
+ "glusterBlockCheckCapabilities() for block %s on volume %s failed",
+ blk->block_name, blk->volume);
+ goto out;
+ }
+
strcpy(mobj.block_name, blk->block_name);
strcpy(mobj.volume, blk->volume);
strcpy(mobj.gbid, info->gbid);
@@ -1610,6 +1995,7 @@ block_modify_cli_1_svc_st(blockModifyCli *blk, struct svc_req *rqstp)
out:
GB_METAUNLOCK(lkfd, blk->volume, ret, errMsg);
+ blockServerDefFree(list);
nolock:
if (lkfd && glfs_close(lkfd) != 0) {
@@ -1796,6 +2182,7 @@ blockCreateCliFormatResponse(struct glfs *glfs, blockCreateCli *blk,
return;
}
+
blockResponse *
block_create_cli_1_svc_st(blockCreateCli *blk, struct svc_req *rqstp)
{
@@ -1837,6 +2224,14 @@ block_create_cli_1_svc_st(blockCreateCli *blk, struct svc_req *rqstp)
goto optfail;
}
+ errCode = glusterBlockCheckCapabilities((void *)blk, CREATE_SRV, list, &errMsg);
+ if (errCode) {
+ LOG("mgmt", GB_LOG_ERROR,
+ "glusterBlockCheckCapabilities() for block %s on volume %s failed",
+ blk->block_name, blk->volume);
+ goto optfail;
+ }
+
glfs = glusterBlockVolumeInit(blk->volume, &errCode, &errMsg);
if (!glfs) {
LOG("mgmt", GB_LOG_ERROR,
@@ -2302,6 +2697,8 @@ block_delete_cli_1_svc_st(blockDeleteCli *blk, struct svc_req *rqstp)
char *errMsg = NULL;
int errCode = 0;
int ret;
+ blockServerDefPtr list = NULL;
+ size_t i;
LOG("mgmt", GB_LOG_INFO, "delete cli request, volume=%s blockname=%s",
@@ -2361,6 +2758,20 @@ block_delete_cli_1_svc_st(blockDeleteCli *blk, struct svc_req *rqstp)
}
}
+ list = glusterBlockGetListFromInfo(info);
+ if (!list) {
+ errCode = ENOMEM;
+ goto out;
+ }
+
+ errCode = glusterBlockCheckCapabilities((void *)blk, DELETE_SRV, list, &errMsg);
+ if (errCode) {
+ LOG("mgmt", GB_LOG_ERROR,
+ "glusterBlockCheckCapabilities() for block %s on volume %s failed",
+ blk->block_name, blk->volume);
+ goto out;
+ }
+
errCode = glusterBlockCleanUp(glfs, blk->block_name, TRUE, TRUE, savereply);
if (errCode) {
LOG("mgmt", GB_LOG_WARNING, "glusterBlockCleanUp: return %d "
@@ -2368,9 +2779,9 @@ block_delete_cli_1_svc_st(blockDeleteCli *blk, struct svc_req *rqstp)
}
out:
- GB_FREE(info);
-
GB_METAUNLOCK(lkfd, blk->volume, errCode, errMsg);
+ blockServerDefFree(list);
+ GB_FREE(info);
optfail:
if (lkfd && glfs_close(lkfd) != 0) {
@@ -2462,6 +2873,22 @@ block_delete_1_svc_st(blockDelete *blk, struct svc_req *rqstp)
return reply;
}
+blockResponse *
+block_version_1_svc_st(void *data, struct svc_req *rqstp)
+{
+ int ret = -1;
+ blockResponse *reply = NULL;
+
+ if (GB_ALLOC(reply) < 0) {
+ return NULL;
+ }
+
+ reply->exit = gbSetCapabilties(&reply);
+
+ GB_ASPRINTF(&reply->out, "In version");
+ return reply;
+
+}
blockResponse *
block_modify_1_svc_st(blockModify *blk, struct svc_req *rqstp)
@@ -2725,23 +3152,6 @@ block_list_cli_1_svc_st(blockListCli *blk, struct svc_req *rqstp)
return reply;
}
-static bool
-blockhostIsValid (char *status)
-{
- switch (blockMetaStatusEnumParse(status)) {
- case GB_CONFIG_SUCCESS:
- case GB_CLEANUP_INPROGRESS:
- case GB_AUTH_ENFORCEING:
- case GB_AUTH_ENFORCED:
- case GB_AUTH_ENFORCE_FAIL:
- case GB_AUTH_CLEAR_ENFORCED:
- case GB_AUTH_CLEAR_ENFORCEING:
- case GB_AUTH_CLEAR_ENFORCE_FAIL:
- return TRUE;
- }
-
- return FALSE;
-}
void
blockInfoCliFormatResponse(blockInfoCli *blk, int errCode,
@@ -2939,6 +3349,16 @@ block_modify_1_svc(blockModify *blk, blockResponse *reply, struct svc_req *rqstp
bool_t
+block_version_1_svc(void *data, blockResponse *reply, struct svc_req *rqstp)
+{
+ int ret;
+
+ GB_RPC_CALL(version, data, reply, rqstp, ret);
+ return ret;
+}
+
+
+bool_t
block_create_cli_1_svc(blockCreateCli *blk, blockResponse *reply,
struct svc_req *rqstp)
{
diff --git a/rpc/rpcl/block.x b/rpc/rpcl/block.x
index 66d1e32..6eeb321 100644
--- a/rpc/rpcl/block.x
+++ b/rpc/rpcl/block.x
@@ -87,6 +87,7 @@ program GLUSTER_BLOCK {
blockResponse BLOCK_CREATE(blockCreate) = 1;
blockResponse BLOCK_DELETE(blockDelete) = 2;
blockResponse BLOCK_MODIFY(blockModify) = 3;
+ blockResponse BLOCK_VERSION() = 4;
} = 1;
} = 21215311; /* B2 L12 O15 C3 K11 */
diff --git a/utils/Makefile.am b/utils/Makefile.am
index bdebb52..6eadf79 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -1,16 +1,27 @@
noinst_LTLIBRARIES = libgb.la
-libgb_la_SOURCES = common.c utils.c lru.c
+libgb_la_SOURCES = common.c utils.c lru.c capabilities.c
-noinst_HEADERS = common.h utils.h lru.h list.h
+noinst_HEADERS = common.h utils.h lru.h list.h capabilities.h
-libgb_la_CFLAGS = $(GFAPI_CFLAGS) -DDATADIR=\"$(localstatedir)\" \
+libgb_la_CFLAGS = $(GFAPI_CFLAGS) \
+ -DDATADIR=\"$(localstatedir)\" -DCONFDIR=\"$(sysconfigdir)\" \
-I$(top_builddir)/ -I$(top_builddir)/rpc/rpcl
libgb_la_LIBADD = $(GFAPI_LIBS)
libgb_ladir = $(includedir)/gluster-block/utils
+EXTRA_DIST = gluster-block-caps.info
+
DISTCLEANFILES = Makefile.in
CLEANFILES = *~
+
+install-data-local:
+ $(MKDIR_P) $(DESTDIR)${sysconfigdir}; \
+ $(INSTALL_DATA) gluster-block-caps.info \
+ $(DESTDIR)${sysconfigdir}/gluster-block-caps.info;
+
+uninstall-local:
+ rm -f $(DESTDIR)${sysconfigdir}/gluster-block-caps.info
diff --git a/utils/capabilities.c b/utils/capabilities.c
new file mode 100644
index 0000000..d44dc8f
--- /dev/null
+++ b/utils/capabilities.c
@@ -0,0 +1,109 @@
+/*
+ Copyright (c) 2018 Red Hat, Inc. <http://www.redhat.com>
+ This file is part of gluster-block.
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+
+# include "capabilities.h"
+
+
+int
+gbCapabilitiesEnumParse(const char *cap)
+{
+ int i;
+
+
+ if (!cap) {
+ return GB_CAP_MAX;
+ }
+
+ for (i = 0; i < GB_CAP_MAX; i++) {
+ if (!strcmp(cap, gbCapabilitiesLookup[i])) {
+ return i;
+ }
+ }
+
+ return i;
+}
+
+
+int
+gbSetCapabilties(blockResponse **c)
+{
+ FILE * fp;
+ char *line = NULL;
+ size_t len = 0;
+ int count = 0;
+ int ret = 0;
+ blockResponse *reply = *c;
+ gbCapObj *caps = NULL;
+ char *p, *sep;
+
+
+ fp = fopen(GB_CAPS_FILE, "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ if (GB_ALLOC_N(caps, GB_CAP_MAX) < 0) {
+ fclose(fp);
+ return -1;
+ }
+
+ while ((getline(&line, &len, fp)) != -1) {
+ if (!line) {
+ continue;
+ }
+ if ((line[0] == '\n') || (line[0] == '#')) {
+ GB_FREE(line);
+ continue;
+ }
+
+ /* Part before ':' */
+ p = line;
+ sep = strchr(p, ':');
+ *sep = '\0';
+
+ ret = gbCapabilitiesEnumParse(p);
+ if (ret != GB_CAP_MAX) {
+ strncpy(caps[count].cap, gbCapabilitiesLookup[ret], 256);
+
+ /* Part after ':' and before '\n' */
+ p = sep + 1;
+ sep = strchr(p, '\n');
+ *sep = '\0';
+
+ while(isspace((unsigned char)*p)) p++;
+ ret = convertStringToTrillianParse(p);
+ if (ret >= 0) {
+ caps[count].status = ret;
+ } else {
+ ret = -1;
+ goto out;
+ }
+ count++;
+ }
+
+ GB_FREE(line);
+ }
+
+ if (GB_CAP_MAX != count) {
+ ret = -1;
+ goto out;
+ }
+
+ reply->xdata.xdata_len = GB_CAP_MAX * sizeof(gbCapObj);
+ reply->xdata.xdata_val = (char *) caps;
+
+ ret = 0;
+ out:
+ GB_FREE(line);
+ fclose(fp);
+
+ return ret;
+}
diff --git a/utils/capabilities.h b/utils/capabilities.h
new file mode 100644
index 0000000..b157d80
--- /dev/null
+++ b/utils/capabilities.h
@@ -0,0 +1,65 @@
+/*
+ Copyright (c) 2018 Red Hat, Inc. <http://www.redhat.com>
+ This file is part of gluster-block.
+
+ This file is licensed to you under your choice of the GNU Lesser
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+
+# include "block.h"
+# include "common.h"
+
+
+typedef struct gbCapObj {
+ char cap[256];
+ bool status;
+} gbCapObj;
+
+
+typedef struct gbCapResp {
+ int capMax;
+ gbCapObj *response;
+} gbCapResp;
+
+
+enum gbCapabilities {
+ GB_CREATE_CAP,
+ GB_CREATE_HA_CAP,
+ GB_CREATE_PREALLOC_CAP,
+ GB_CREATE_AUTH_CAP,
+
+ GB_DELETE_CAP,
+ GB_DELETE_FORCE_CAP,
+
+ GB_MODIFY_CAP,
+ GB_MODIFY_AUTH_CAP,
+
+ GB_JSON_CAP,
+
+ GB_CAP_MAX
+};
+
+
+static const char *const gbCapabilitiesLookup[] = {
+ [GB_CREATE_CAP] = "create",
+ [GB_CREATE_HA_CAP] = "create_ha",
+ [GB_CREATE_PREALLOC_CAP] = "create_prealloc",
+ [GB_CREATE_AUTH_CAP] = "create_auth",
+
+ [GB_DELETE_CAP] = "delete",
+ [GB_DELETE_FORCE_CAP] = "delete_force",
+
+ [GB_MODIFY_CAP] = "modify",
+ [GB_MODIFY_AUTH_CAP] = "modify_auth",
+
+ [GB_JSON_CAP] = "json",
+
+ [GB_CAP_MAX] = NULL
+};
+
+
+int gbCapabilitiesEnumParse(const char *cap);
+int gbSetCapabilties (blockResponse **c);
diff --git a/utils/gluster-block-caps.info b/utils/gluster-block-caps.info
new file mode 100644
index 0000000..2dcdbd4
--- /dev/null
+++ b/utils/gluster-block-caps.info
@@ -0,0 +1,98 @@
+##
+# Nature: cli command
+#
+# Label: 'create'
+#
+# Description: capability to create block storage in local node
+#
+# Since: 0.1
+##
+create: true
+
+##
+# Nature: cli sub-command
+#
+# Label: 'ha'
+#
+# Description: capability to replicate configuration of block for high availability
+#
+# Since: 0.1
+##
+create_ha: true
+
+##
+# Nature: cli sub-command
+#
+# Label: 'prealloc'
+#
+# Description: capability to preallocate underline storage
+#
+# Since: 0.3
+##
+create_prealloc: true
+
+##
+# Nature: cli sub-command
+#
+# Label: 'auth'
+#
+# Description: capability to set CHAP authentication for block
+#
+# Since: 0.2
+##
+create_auth: true
+
+##
+# Nature: cli command
+#
+# Label: 'delete'
+#
+# Description: capability to delete a block storage
+#
+# Since: 0.1
+##
+delete: true
+
+##
+# Nature: cli command
+#
+# Label: 'force'
+#
+# Description: capability to delete a block storage forcefully
+#
+# Since: 0.1
+##
+delete_force: true
+
+##
+# Nature: cli command
+#
+# Label: 'modify'
+#
+# Description: capability to modify already existing block
+#
+# Since: 0.2
+##
+modify: true
+
+##
+# Nature: cli sub-command
+#
+# Label: 'auth'
+#
+# Description: capability to modify CHAP authentication caps for existing block
+#
+# Since: 0.2
+##
+modify_auth: true
+
+##
+# Nature: cli sub-command
+#
+# Label: '--json'
+#
+# Description: capability to furnish results of any command in json formated way
+#
+# Since: 0.2
+##
+json: true
diff --git a/utils/utils.h b/utils/utils.h
index 8a30d25..7141a3f 100644
--- a/utils/utils.h
+++ b/utils/utils.h
@@ -31,6 +31,9 @@
# define GB_LOCK_FILE GB_INFODIR "/gluster-blockd.lock"
# define GB_UNIX_ADDRESS GB_INFODIR "/gluster-blockd.socket"
+
+# define GB_CAPS_FILE CONFDIR "/gluster-block-caps.info"
+
# define GB_TCP_PORT 24010
# define GB_TCP_PORT_STR "24010"
@@ -59,6 +62,11 @@
# define FAILED_CREATING_FILE "failed while creating block file in gluster volume"
# define FAILED_CREATING_META "failed while creating block meta file from volume"
+/* Target Capabilities */
+# define FAILED_CAPS "failed in capabilities check"
+# define FAILED_REMOTE_CAPS "failed in remote capabilities check"
+# define FAILED_REMOTE_AYNC_CAPS "failed in remote async capabilities check"
+
/* Target List */
# define FAILED_LIST "failed in list"