From 5eb8bac561b7374589bd72d597ed7eec95aa7de6 Mon Sep 17 00:00:00 2001 From: Kaushal M Date: Fri, 14 Sep 2012 11:45:34 +0530 Subject: glusterd, cli: Task id's for async tasks This patch introduces task-id's for async tasks like rebalance, remove-brick and replace-brick. An id is generated for each task when it is started and displayed to the user in cli output. The status of running tasks is also included in the output of "volume status" along with its id, so that a user can easily track the progress of an async task. Also, * added tests for this feature into the regression test suite. * added a python script for creating files, 'create-files.py', courtesy Vijaykumar Koppad (vkoppad@redhat.com) into the test suite. This patch reverts the revert commit 698deb33d731df6de84da8ae8ee4045e1543a168. BUG: 857330 Change-Id: Id43d7cb629a38f47f733fbc18cb4c5f2f0327c7a Signed-off-by: Kaushal M Reviewed-on: http://review.gluster.org/4294 Tested-by: Gluster Build System Reviewed-by: Anand Avati --- cli/src/cli-rpc-ops.c | 213 +++++++++++++---- cli/src/cli-xml-output.c | 101 ++++++++ libglusterfs/src/glusterfs.h | 4 + tests/bugs/bug-857330/common.rc | 70 ++++++ tests/bugs/bug-857330/normal.t | 78 ++++++ tests/bugs/bug-857330/xml.t | 101 ++++++++ tests/utils/create-files.py | 207 ++++++++++++++++ xlators/mgmt/glusterd/src/glusterd-brick-ops.c | 161 +++++++++---- xlators/mgmt/glusterd/src/glusterd-handler.c | 2 +- xlators/mgmt/glusterd/src/glusterd-handshake.c | 2 + xlators/mgmt/glusterd/src/glusterd-op-sm.c | 177 ++++++++++++-- xlators/mgmt/glusterd/src/glusterd-rebalance.c | 185 ++++++++++---- xlators/mgmt/glusterd/src/glusterd-replace-brick.c | 219 +++++++++++------ xlators/mgmt/glusterd/src/glusterd-store.c | 73 ++++-- xlators/mgmt/glusterd/src/glusterd-store.h | 1 + xlators/mgmt/glusterd/src/glusterd-utils.c | 265 ++++++++++++++++----- xlators/mgmt/glusterd/src/glusterd-utils.h | 6 + xlators/mgmt/glusterd/src/glusterd-volume-ops.c | 4 +- xlators/mgmt/glusterd/src/glusterd.c | 27 +++ xlators/mgmt/glusterd/src/glusterd.h | 40 +++- 20 files changed, 1607 insertions(+), 329 deletions(-) create mode 100644 tests/bugs/bug-857330/common.rc create mode 100755 tests/bugs/bug-857330/normal.t create mode 100755 tests/bugs/bug-857330/xml.t create mode 100755 tests/utils/create-files.py diff --git a/cli/src/cli-rpc-ops.c b/cli/src/cli-rpc-ops.c index c0909cd0d..22a69da73 100644 --- a/cli/src/cli-rpc-ops.c +++ b/cli/src/cli-rpc-ops.c @@ -1144,6 +1144,7 @@ gf_cli_defrag_volume_cbk (struct rpc_req *req, struct iovec *iov, uint64_t failures = 0; double elapsed = 0; char *size_str = NULL; + char *task_id_str = NULL; if (-1 == req->rpc_status) { goto out; @@ -1193,15 +1194,24 @@ gf_cli_defrag_volume_cbk (struct rpc_req *req, struct iovec *iov, } } - if (!((cmd == GF_DEFRAG_CMD_STOP) || (cmd == GF_DEFRAG_CMD_STATUS))) { - /* All other possibility is about starting a volume */ - if (rsp.op_ret && strcmp (rsp.op_errstr, "")) + if (!((cmd == GF_DEFRAG_CMD_STOP) || (cmd == GF_DEFRAG_CMD_STATUS)) && + !(global_state->mode & GLUSTER_MODE_XML)) { + /* All other possibilites are about starting a rebalance */ + ret = dict_get_str (dict, GF_REBALANCE_TID_KEY, &task_id_str); + if (rsp.op_ret && strcmp (rsp.op_errstr, "")) { snprintf (msg, sizeof (msg), "%s", rsp.op_errstr); - else - snprintf (msg, sizeof (msg), - "Starting rebalance on volume %s has been %s", - volname, (rsp.op_ret) ? "unsuccessful": - "successful"); + } else { + if (!rsp.op_ret) { + snprintf (msg, sizeof (msg), + "Starting rebalance on volume %s has " + "been successful.\nID: %s", volname, + task_id_str); + } else { + snprintf (msg, sizeof (msg), + "Starting rebalance on volume %s has " + "been unsuccessful.", volname); + } + } goto done; } @@ -1743,6 +1753,8 @@ gf_cli_remove_brick_cbk (struct rpc_req *req, struct iovec *iov, char *cmd_str = "unknown"; cli_local_t *local = NULL; call_frame_t *frame = NULL; + char *task_id_str = NULL; + dict_t *rsp_dict = NULL; if (-1 == req->rpc_status) { goto out; @@ -1763,10 +1775,32 @@ gf_cli_remove_brick_cbk (struct rpc_req *req, struct iovec *iov, goto out; } - switch (cmd) { + if (rsp.dict.dict_len) { + rsp_dict = dict_new (); + if (!rsp_dict) { + ret = -1; + goto out; + } + + ret = dict_unserialize (rsp.dict.dict_val, rsp.dict.dict_len, + &rsp_dict); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Failed to unserialize rsp_dict"); + goto out; + } + } + switch (cmd) { case GF_OP_CMD_START: cmd_str = "start"; + + ret = dict_get_str (rsp_dict, GF_REMOVE_BRICK_TID_KEY, &task_id_str); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "remove-brick-id is not present in dict"); + goto out; + } break; case GF_OP_CMD_COMMIT: cmd_str = "commit"; @@ -1788,7 +1822,7 @@ gf_cli_remove_brick_cbk (struct rpc_req *req, struct iovec *iov, (rsp.op_ret) ? "unsuccessful": "successful"); if (global_state->mode & GLUSTER_MODE_XML) { - ret = cli_xml_output_vol_remove_brick (_gf_false, NULL, + ret = cli_xml_output_vol_remove_brick (_gf_false, rsp_dict, rsp.op_ret, rsp.op_errno, rsp.op_errstr); if (ret) @@ -1797,10 +1831,14 @@ gf_cli_remove_brick_cbk (struct rpc_req *req, struct iovec *iov, goto out; } - if (rsp.op_ret) - cli_err ("volume remove-brick: failed: %s", rsp.op_errstr); - else - cli_out ("volume remove-brick: success"); + if (rsp.op_ret) { + cli_err ("volume remove-brick %s: failed: %s", cmd_str, + rsp.op_errstr); + } else { + cli_out ("volume remove-brick %s: success", cmd_str); + if (GF_OP_CMD_START == cmd) + cli_out ("ID: %s", task_id_str); + } ret = rsp.op_ret; @@ -1829,7 +1867,8 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, gf1_cli_replace_op replace_op = 0; char *rb_operation_str = NULL; dict_t *rsp_dict = NULL; - char msg[1024] = {0,}; + char msg[1024] = {0,}; + char *task_id_str = NULL; if (-1 == req->rpc_status) { goto out; @@ -1854,33 +1893,48 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, goto out; } + if (rsp.dict.dict_len) { + /* Unserialize the dictionary */ + rsp_dict = dict_new (); + + ret = dict_unserialize (rsp.dict.dict_val, + rsp.dict.dict_len, + &rsp_dict); + if (ret < 0) { + gf_log ("glusterd", GF_LOG_ERROR, + "failed to " + "unserialize rsp buffer to dictionary"); + goto out; + } + } + switch (replace_op) { case GF_REPLACE_OP_START: - if (rsp.op_ret) - rb_operation_str = "replace-brick failed to start"; - else - rb_operation_str = "replace-brick started successfully"; + if (rsp.op_ret) { + rb_operation_str = gf_strdup ("replace-brick failed to" + " start"); + } else { + ret = dict_get_str (rsp_dict, GF_REPLACE_BRICK_TID_KEY, + &task_id_str); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "Failed to get " + "\"replace-brick-id\" from dict"); + goto out; + } + ret = gf_asprintf (&rb_operation_str, + "replace-brick started successfully" + "\nID: %s", task_id_str); + if (ret < 0) + goto out; + } break; case GF_REPLACE_OP_STATUS: - if (rsp.op_ret || ret) - rb_operation_str = "replace-brick status unknown"; - else { - if (rsp.dict.dict_len) { - /* Unserialize the dictionary */ - rsp_dict = dict_new (); - - ret = dict_unserialize (rsp.dict.dict_val, - rsp.dict.dict_len, - &rsp_dict); - if (ret < 0) { - gf_log ("glusterd", GF_LOG_ERROR, - "failed to " - "unserialize req-buffer to dictionary"); - goto out; - } - } + if (rsp.op_ret || ret) { + rb_operation_str = gf_strdup ("replace-brick status " + "unknown"); + } else { ret = dict_get_str (rsp_dict, "status-reply", &status_reply); if (ret) { @@ -1889,23 +1943,27 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, goto out; } - rb_operation_str = status_reply; + rb_operation_str = gf_strdup (status_reply); } break; case GF_REPLACE_OP_PAUSE: if (rsp.op_ret) - rb_operation_str = "replace-brick pause failed"; + rb_operation_str = gf_strdup ("replace-brick pause " + "failed"); else - rb_operation_str = "replace-brick paused successfully"; + rb_operation_str = gf_strdup ("replace-brick paused " + "successfully"); break; case GF_REPLACE_OP_ABORT: if (rsp.op_ret) - rb_operation_str = "replace-brick abort failed"; + rb_operation_str = gf_strdup ("replace-brick abort " + "failed"); else - rb_operation_str = "replace-brick aborted successfully"; + rb_operation_str = gf_strdup ("replace-brick aborted " + "successfully"); break; case GF_REPLACE_OP_COMMIT: @@ -1926,9 +1984,11 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, if (rsp.op_ret || ret) - rb_operation_str = "replace-brick commit failed"; + rb_operation_str = gf_strdup ("replace-brick commit " + "failed"); else - rb_operation_str = "replace-brick commit successful"; + rb_operation_str = gf_strdup ("replace-brick commit " + "successful"); break; @@ -1939,7 +1999,7 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, } if (rsp.op_ret && (strcmp (rsp.op_errstr, ""))) { - rb_operation_str = rsp.op_errstr; + rb_operation_str = gf_strdup (rsp.op_errstr); } gf_log ("cli", GF_LOG_INFO, "Received resp to replace brick"); @@ -1963,6 +2023,17 @@ gf_cli_replace_brick_cbk (struct rpc_req *req, struct iovec *iov, ret = rsp.op_ret; out: + if (frame) + frame->local = NULL; + + if (local) { + dict_unref (local->dict); + cli_local_wipe (local); + } + + if (rb_operation_str) + GF_FREE (rb_operation_str); + cli_cmd_broadcast_response (ret); free (rsp.dict.dict_val); if (rsp_dict) @@ -3034,7 +3105,7 @@ gf_cli_remove_brick (call_frame_t *frame, xlator_t *this, GLUSTER_CLI_REMOVE_BRICK, this, cli_rpc_prog, NULL); } else { - /* Need rebalance status to e sent :-) */ + /* Need rebalance status to be sent :-) */ req_dict = dict_new (); if (!req_dict) { ret = -1; @@ -5276,6 +5347,55 @@ out: return; } + +static void +cli_print_volume_tasks (dict_t *dict) { + int ret = -1; + int tasks = 0; + char *op = 0; + char *task_id_str = NULL; + int status = 0; + char key[1024] = {0,}; + int i = 0; + + ret = dict_get_int32 (dict, "tasks", &tasks); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, + "Failed to get tasks count"); + return; + } + + if (tasks == 0) { + cli_out ("There are no active volume tasks"); + return; + } + + cli_out ("%15s%40s%15s", "Task", "ID", "Status"); + cli_out ("%15s%40s%15s", "----", "--", "------"); + for (i = 0; i < tasks; i++) { + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.type", i); + ret = dict_get_str(dict, key, &op); + if (ret) + return; + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.id", i); + ret = dict_get_str (dict, key, &task_id_str); + if (ret) + return; + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.status", i); + ret = dict_get_int32 (dict, key, &status); + if (ret) + return; + + cli_out ("%15s%40s%15d", op, task_id_str, status); + } + +} + static int gf_cli_status_cbk (struct rpc_req *req, struct iovec *iov, int count, void *myframe) @@ -5522,6 +5642,9 @@ gf_cli_status_cbk (struct rpc_req *req, struct iovec *iov, } } cli_out (" "); + + if ((cmd & GF_CLI_STATUS_MASK) == GF_CLI_STATUS_NONE) + cli_print_volume_tasks (dict); cont: ret = rsp.op_ret; diff --git a/cli/src/cli-xml-output.c b/cli/src/cli-xml-output.c index e43932662..88410ef57 100644 --- a/cli/src/cli-xml-output.c +++ b/cli/src/cli-xml-output.c @@ -1386,6 +1386,74 @@ out: #endif } +int +cli_xml_output_vol_status_tasks (cli_local_t *local, dict_t *dict) { + int ret = -1; + char *task_type = NULL; + char *task_id_str = NULL; + int status = 0; + int tasks = 0; + char key[1024] = {0,}; + int i = 0; + + /* */ + ret = xmlTextWriterStartElement (local->writer, (xmlChar *)"tasks"); + XML_RET_CHECK_AND_GOTO (ret, out); + + ret = dict_get_int32 (dict, "tasks", &tasks); + if (ret) + goto out; + + for (i = 0; i < tasks; i++) { + /* */ + ret = xmlTextWriterStartElement (local->writer, + (xmlChar *)"task"); + XML_RET_CHECK_AND_GOTO (ret, out); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.type", i); + ret = dict_get_str (dict, key, &task_type); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (local->writer, + (xmlChar *)"type", + "%s", task_type); + XML_RET_CHECK_AND_GOTO (ret, out); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.id", i); + ret = dict_get_str (dict, key, &task_id_str); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (local->writer, + (xmlChar *)"id", + "%s", task_id_str); + XML_RET_CHECK_AND_GOTO (ret, out); + + memset (key, 0, sizeof (key)); + snprintf (key, sizeof (key), "task%d.status", i); + ret = dict_get_int32 (dict, key, &status); + if (ret) + goto out; + ret = xmlTextWriterWriteFormatElement (local->writer, + (xmlChar *)"status", + "%d", status); + XML_RET_CHECK_AND_GOTO (ret, out); + + /* */ + ret = xmlTextWriterEndElement (local->writer); + XML_RET_CHECK_AND_GOTO (ret, out); + } + + /* */ + ret = xmlTextWriterEndElement (local->writer); + + +out: + gf_log ("cli", GF_LOG_DEBUG, "Returning %d", ret); + return ret; +} + int cli_xml_output_vol_status (cli_local_t *local, dict_t *dict) { @@ -1513,6 +1581,12 @@ cli_xml_output_vol_status (cli_local_t *local, dict_t *dict) XML_RET_CHECK_AND_GOTO (ret, out); } + if ((cmd & GF_CLI_STATUS_MASK) == GF_CLI_STATUS_NONE) { + ret = cli_xml_output_vol_status_tasks (local, dict); + if (ret) + goto out; + } + /* */ ret = xmlTextWriterEndElement (local->writer); XML_RET_CHECK_AND_GOTO (ret, out); @@ -2919,6 +2993,7 @@ cli_xml_output_vol_rebalance (gf_cli_defrag_type op, dict_t *dict, int op_ret, int ret = -1; xmlTextWriterPtr writer = NULL; xmlBufferPtr buf = NULL; + char *task_id_str = NULL; ret = cli_begin_xml_output (&writer, &buf); if (ret) @@ -2932,6 +3007,14 @@ cli_xml_output_vol_rebalance (gf_cli_defrag_type op, dict_t *dict, int op_ret, ret = xmlTextWriterStartElement (writer, (xmlChar *)"volRebalance"); XML_RET_CHECK_AND_GOTO (ret, out); + ret = dict_get_str (dict, GF_REBALANCE_TID_KEY, &task_id_str); + if (ret == 0) { + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"task-id", + "%s", task_id_str); + XML_RET_CHECK_AND_GOTO (ret, out); + } + ret = xmlTextWriterWriteFormatElement (writer, (xmlChar *)"op", "%d", op); XML_RET_CHECK_AND_GOTO (ret, out); @@ -2965,6 +3048,7 @@ cli_xml_output_vol_remove_brick (gf_boolean_t status_op, dict_t *dict, int ret = -1; xmlTextWriterPtr writer = NULL; xmlBufferPtr buf = NULL; + char *task_id_str = NULL; ret = cli_begin_xml_output (&writer, &buf); if (ret) @@ -2978,6 +3062,14 @@ cli_xml_output_vol_remove_brick (gf_boolean_t status_op, dict_t *dict, ret = xmlTextWriterStartElement (writer, (xmlChar *)"volRemoveBrick"); XML_RET_CHECK_AND_GOTO (ret, out); + ret = dict_get_str (dict, GF_REMOVE_BRICK_TID_KEY, &task_id_str); + if (ret == 0) { + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"task-id", + "%s", task_id_str); + XML_RET_CHECK_AND_GOTO (ret, out); + } + if (status_op) { ret = cli_xml_output_vol_rebalance_status (writer, dict); if (ret) @@ -3008,6 +3100,7 @@ cli_xml_output_vol_replace_brick (gf1_cli_replace_op op, dict_t *dict, int status = 0; uint64_t files = 0; char *current_file = 0; + char *task_id_str = NULL; xmlTextWriterPtr writer = NULL; xmlBufferPtr buf = NULL; @@ -3023,6 +3116,14 @@ cli_xml_output_vol_replace_brick (gf1_cli_replace_op op, dict_t *dict, ret = xmlTextWriterStartElement (writer, (xmlChar *)"volReplaceBrick"); XML_RET_CHECK_AND_GOTO (ret, out); + ret = dict_get_str (dict, GF_REPLACE_BRICK_TID_KEY, &task_id_str); + if (ret == 0) { + ret = xmlTextWriterWriteFormatElement (writer, + (xmlChar *)"task-id", + "%s", task_id_str); + XML_RET_CHECK_AND_GOTO (ret, out); + } + ret = xmlTextWriterWriteFormatElement (writer, (xmlChar *)"op", "%d", op); XML_RET_CHECK_AND_GOTO (ret, out); diff --git a/libglusterfs/src/glusterfs.h b/libglusterfs/src/glusterfs.h index 61edd5ec7..dae539841 100644 --- a/libglusterfs/src/glusterfs.h +++ b/libglusterfs/src/glusterfs.h @@ -142,6 +142,10 @@ #define GF_UUID_BUF_SIZE 50 +#define GF_REBALANCE_TID_KEY "rebalance-id" +#define GF_REMOVE_BRICK_TID_KEY "remove-brick-id" +#define GF_REPLACE_BRICK_TID_KEY "replace-brick-id" + /* NOTE: add members ONLY at the end (just before _MAXVALUE) */ typedef enum { GF_FOP_NULL = 0, diff --git a/tests/bugs/bug-857330/common.rc b/tests/bugs/bug-857330/common.rc new file mode 100644 index 000000000..f2327a862 --- /dev/null +++ b/tests/bugs/bug-857330/common.rc @@ -0,0 +1,70 @@ +. $(dirname $0)/../../include.rc + +UUID_REGEX='[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}' + +TASK_ID="" +COMMAND="" +PATTERN="" + +function check-and-store-task-id() +{ + TASK_ID="" + + local task_id=$($CLI $COMMAND | grep $PATTERN | grep -o -E "$UUID_REGEX") + + if [ -z "$task_id" ] && [ "${task_id+asdf}" = "asdf" ]; then + return 1 + fi + + TASK_ID=$task_id + return 0; +} + +function check-with-stored-task-id() +{ + local task_id=$($CLI $COMMAND | grep $PATTERN | grep -o -E "$UUID_REGEX") + + if [ -z "$task_id" ] && [ "${task_id+asdf}" = "asdf" ]; then + return 1 + fi + + if [ "$TASK_ID" != "$task_id" ]; then + return 1 + fi + + return 0 +} + +function check-and-store-task-id-xml() +{ + TASK_ID="" + + local task_id=$($CLI $COMMAND --xml | xmllint --format - | grep $PATTERN | grep -o -E "$UUID_REGEX") + + if [ -z "$task_id" ] && [ "${task_id+asdf}" = "asdf" ]; then + return 1 + fi + + TASK_ID=$task_id + return 0; +} + +function check-with-stored-task-id-xml() +{ + local task_id=$($CLI $COMMAND --xml | xmllint --format - | grep $PATTERN | grep -o -E "$UUID_REGEX") + + if [ -z "$task_id" ] && [ "${task_id+asdf}" = "asdf" ]; then + return 1 + fi + + if [ "$TASK_ID" != "$task_id" ]; then + return 1 + fi + + return 0 +} + +function get-task-status() +{ + $CLI $COMMAND | grep -o $PATTERN +} diff --git a/tests/bugs/bug-857330/normal.t b/tests/bugs/bug-857330/normal.t new file mode 100755 index 000000000..abf8e2ac5 --- /dev/null +++ b/tests/bugs/bug-857330/normal.t @@ -0,0 +1,78 @@ +#!/bin/bash + +. $(dirname $0)/common.rc + +cleanup; + +TEST glusterd +TEST pidof glusterd +TEST $CLI volume info; + +TEST $CLI volume create $V0 $H0:$B0/${V0}1; +TEST $CLI volume info $V0; +TEST $CLI volume start $V0; + +TEST glusterfs -s $H0 --volfile-id=$V0 $M0; + +TEST python2 $(dirname $0)/../../utils/create-files.py --multi -b 10 -d 10 -n 10 $M0; + +TEST umount $M0; + +############### +## Rebalance ## +############### +TEST $CLI volume add-brick $V0 $H0:$B0/${V0}2; + +COMMAND="volume rebalance $V0 start" +PATTERN="ID:" +TEST check-and-store-task-id + +COMMAND="volume status $V0" +PATTERN="Rebalance" +TEST check-with-stored-task-id + +COMMAND="volume rebalance $V0 status" +PATTERN="completed" +EXPECT_WITHIN 300 $PATTERN get-task-status + +################### +## Replace-brick ## +################### +REP_BRICK_PAIR="$H0:$B0/${V0}2 $H0:$B0/${V0}3" + +COMMAND="volume replace-brick $V0 $REP_BRICK_PAIR start" +PATTERN="ID:" +TEST check-and-store-task-id + +COMMAND="volume status $V0" +PATTERN="Replace" +TEST check-with-stored-task-id + +COMMAND="volume replace-brick $V0 $REP_BRICK_PAIR status" +PATTERN="complete" +EXPECT_WITHIN 300 $PATTERN get-task-status + +TEST $CLI volume replace-brick $V0 $REP_BRICK_PAIR commit; + +################## +## Remove-brick ## +################## +COMMAND="volume remove-brick $V0 $H0:$B0/${V0}3 start" +PATTERN="ID:" +TEST check-and-store-task-id + +COMMAND="volume status $V0" +PATTERN="Remove" +TEST check-with-stored-task-id + +COMMAND="volume remove-brick $V0 $H0:$B0/${V0}3 status" +PATTERN="completed" +EXPECT_WITHIN 300 $PATTERN get-task-status + +TEST $CLI volume remove-brick $V0 $H0:$B0/${V0}3 commit + +TEST $CLI volume stop $V0; +TEST $CLI volume delete $V0; +TEST ! $CLI volume info $V0; + +cleanup; diff --git a/tests/bugs/bug-857330/xml.t b/tests/bugs/bug-857330/xml.t new file mode 100755 index 000000000..a6e0b34ca --- /dev/null +++ b/tests/bugs/bug-857330/xml.t @@ -0,0 +1,101 @@ +#!/bin/bash + +. $(dirname $0)/common.rc + +cleanup; + +TEST glusterd +TEST pidof glusterd +TEST $CLI volume info; + +TEST $CLI volume create $V0 $H0:$B0/${V0}1; +TEST $CLI volume info $V0; +TEST $CLI volume start $V0; + +TEST glusterfs -s $H0 --volfile-id=$V0 $M0; + +TEST python2 $(dirname $0)/../../utils/create-files.py --multi -b 10 -d 10 -n 10 $M0; + +TEST umount $M0; + + +############### +## Rebalance ## +############### +TEST $CLI volume add-brick $V0 $H0:$B0/${V0}2; + +COMMAND="volume rebalance $V0 start" +PATTERN="task-id" +TEST check-and-store-task-id-xml + +COMMAND="volume status $V0" +PATTERN="id" +TEST check-with-stored-task-id-xml + +COMMAND="volume rebalance $V0 status" +PATTERN="task-id" +TEST check-with-stored-task-id-xml + +## TODO: Add tests for rebalance stop + +COMMAND="volume rebalance $V0 status" +PATTERN="completed" +EXPECT_WITHIN 300 $PATTERN get-task-status + +################### +## Replace-brick ## +################### +REP_BRICK_PAIR="$H0:$B0/${V0}2 $H0:$B0/${V0}3" + +COMMAND="volume replace-brick $V0 $REP_BRICK_PAIR start" +PATTERN="task-id" +TEST check-and-store-task-id-xml + +COMMAND="volume status $V0" +PATTERN="id" +TEST check-with-stored-task-id-xml + +COMMAND="volume replace-brick $V0 $REP_BRICK_PAIR status" +PATTERN="task-id" +TEST check-with-stored-task-id-xml + +## TODO: Add more tests for replace-brick pause|abort + +COMMAND="volume replace-brick $V0 $REP_BRICK_PAIR status" +PATTERN="complete" +EXPECT_WITHIN 300 $PATTERN get-task-status + +COMMAND="volume replace-brick $V0 $REP_BRICK_PAIR commit" +PATTERN="task-id" +TEST check-with-stored-task-id-xml + +################## +## Remove-brick ## +################## +COMMAND="volume remove-brick $V0 $H0:$B0/${V0}3 start" +PATTERN="task-id" +TEST check-and-store-task-id-xml + +COMMAND="volume status $V0" +PATTERN="id" +TEST check-with-stored-task-id-xml + +COMMAND="volume remove-brick $V0 $H0:$B0/${V0}3 status" +PATTERN="task-id" +TEST check-with-stored-task-id-xml + +COMMAND="volume remove-brick $V0 $H0:$B0/${V0}3 status" +PATTERN="completed" +EXPECT_WITHIN 300 $PATTERN get-task-status + +## TODO: Add tests for remove-brick stop + +COMMAND="volume remove-brick $V0 $H0:$B0/${V0}3 commit" +PATTERN="task-id" +TEST check-with-stored-task-id-xml + +TEST $CLI volume stop $V0; +TEST $CLI volume delete $V0; +TEST ! $CLI volume info $V0; + +cleanup; diff --git a/tests/utils/create-files.py b/tests/utils/create-files.py new file mode 100755 index 000000000..0d937eff9 --- /dev/null +++ b/tests/utils/create-files.py @@ -0,0 +1,207 @@ +#!/usr/bin/python + +# This script was developed by Vijaykumar Koppad (vkoppad@redhat.com) +# The latest version of this script can found at +# http://github.com/vijaykumar-koppad/crefi + +from __future__ import with_statement +import sys +import os +import re +import random +from optparse import OptionParser +import time +import string +import errno + +def os_rd(src, size): + fd = os.open(src,os.O_RDONLY) + data = os.read(fd, size) + os.close(fd) + return data + +def os_wr(dest, data): + fd = os.open(dest,os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0644) + os.write(fd, data) + os.close(fd) + return + +def create_sparse_file(fil): + if option.size: + option.random = False + size = option.size + else: + size = random.randint(option.min, option.max) + data = os_rd("/dev/zero", size) + os_wr(fil, data) + return + +def create_binary_file(fil): + if option.size: + option.random = False + size = option.size + else: + size = random.randint(option.min, option.max) + data = os_rd("/dev/urandom", size) + os_wr(fil, data) + return + +def create_txt_file(fil): + if option.size: + option.random = False + size = option.size + else: + size = random.randint(option.min, option.max) + if size < 500*1024: + data = os_rd("/etc/services", size) + os_wr(fil, data) + else: + data = os_rd("/etc/services", 500*1024) + file_size = 0 + fd = os.open(fil,os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0644) + while file_size < size: + os.write(fd, data) + file_size += 500*1024 + os.close(fd) + return + +def get_filename(): + size = option.flen + char = string.uppercase+string.digits + st = ''.join(random.choice(char) for i in range(size)) + ti = str((hex(int(str(time.time()).split('.')[0])))[2:]) + return ti+"~~"+st + +def text_files(files, file_count): + for k in range(files): + if not file_count%option.inter: + print file_count + fil = get_filename() + create_txt_file(fil) + file_count += 1 + return file_count + +def sparse_files(files, file_count): + for k in range(files): + if not file_count%option.inter: + print file_count + fil = get_filename() + create_sparse_file(fil) + file_count += 1 + return file_count + +def binary_files(files, file_count): + for k in range(files): + if not file_count%option.inter: + print file_count + fil = get_filename() + create_binary_file(fil) + file_count += 1 + return file_count + +def human2bytes(size): + size_short = { + 1024 : ['K','KB','KiB','k','kB','kiB'], + 1024*1024 : ['M','MB','MiB'], + 1024*1024*1024 : ['G','GB','GiB'] +} + num = re.search('(\d+)',size).group() + ext = size[len(num):] + num = int(num) + if ext == '': + return num + for value, keys in size_short.items(): + if ext in keys: + size = num*value + return size + +def multipledir(mnt_pnt,brdth,depth,files): + files_count = 1 + for i in range(brdth): + breadth = mnt_pnt+"/"+str(i) + try: + os.makedirs(breadth) + except OSError as ex: + if not ex.errno is errno.EEXIST: + raise + os.chdir(breadth) + dir_depth = breadth + print breadth + for j in range(depth): + dir_depth = dir_depth+"/"+str(j) + try: + os.makedirs(dir_depth) + except OSError as ex: + if not ex.errno is errno.EEXIST: + raise + os.chdir(dir_depth) + if option.file_type == "text": + files_count = text_files(files, files_count) + elif option.file_type == "sparse": + files_count = sparse_files(files, files_count) + elif option.file_type == "binary": + files_count = binary_files(files, files_count) + else: + print "Not a valid file type" + sys.exit(1) + +def singledir(mnt_pnt, files): + files_count = 1 + os.chdir(mnt_pnt) + if option.file_type == "text": + files_count = text_files(files, files_count) + elif option.file_type == "sparse": + files_count = sparse_files(files, files_count) + elif option.file_type == "binary": + files_count = binary_files(files, files_count) + else: + print "Not a valid file type" + sys.exit(1) + +if __name__ == '__main__': + usage = "usage: %prog [option] " + parser = OptionParser(usage=usage) + parser.add_option("-n", dest="files",type="int" ,default=100, + help="number of files in each level [default: %default]") + parser.add_option("--size", action = "store",type="string", + help="size of the files to be used") + parser.add_option("--random", action="store_true", default=True, + help="random size of the file between --min and --max " + "[default: %default]") + parser.add_option("--max", action = "store",type="string", default="500K", + help="maximum size of the files, if random is True " + "[default: %default]") + parser.add_option("--min", action = "store",type="string", default="10K", + help="minimum size of the files, if random is True " + "[default: %default]" ) + parser.add_option("--single", action="store_true", dest="dir",default=True, + help="create files in single directory [default: %default]" ) + parser.add_option("--multi", action="store_false", dest="dir", + help="create files in multiple directories") + parser.add_option("-b", dest="brdth",type="int",default=5, + help="number of directories in one level(works with --multi)[default: %default]") + parser.add_option("-d", dest="depth",type="int",default=5, + help="number of levels of directories(works with --multi)[default: %default]") + parser.add_option("-l", dest="flen",type="int" ,default=10, + help="number of bytes for filename " + "[default: %default]") + parser.add_option("-t","--type", action="store", type="string" , dest="file_type",default="text", + help="type of the file to be created (text, sparse, binary) [default: %default]" ) + parser.add_option("-I", dest="inter", type="int", default=100, + help="print number files created of interval [defailt: %dafault]") + (option,args) = parser.parse_args() + if not args: + print "usage: