collie.c becomes large and we expect that collie will support more commands in future, so let's split the code for readability. Signed-off-by: MORITA Kazutaka <morita.kazutaka at lab.ntt.co.jp> --- collie/Makefile.am | 4 +- collie/cluster.c | 188 +++++++ collie/collie.c | 1573 +--------------------------------------------------- collie/collie.h | 77 +++ collie/common.c | 197 +++++++ collie/node.c | 132 +++++ collie/vdi.c | 1025 ++++++++++++++++++++++++++++++++++ 7 files changed, 1648 insertions(+), 1548 deletions(-) create mode 100644 collie/cluster.c create mode 100644 collie/collie.h create mode 100644 collie/common.c create mode 100644 collie/node.c create mode 100644 collie/vdi.c diff --git a/collie/Makefile.am b/collie/Makefile.am index 82fde03..477b150 100644 --- a/collie/Makefile.am +++ b/collie/Makefile.am @@ -23,11 +23,11 @@ INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include sbin_PROGRAMS = collie -collie_SOURCES = collie.c treeview.c +collie_SOURCES = collie.c common.c treeview.c vdi.c node.c cluster.c collie_LDADD = ../lib/libsheepdog.a collie_DEPENDENCIES = ../lib/libsheepdog.a -noinst_HEADERS = treeview.h +noinst_HEADERS = treeview.h collie.h EXTRA_DIST = diff --git a/collie/cluster.c b/collie/cluster.c new file mode 100644 index 0000000..0d5dfbe --- /dev/null +++ b/collie/cluster.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <time.h> +#include <sys/time.h> + +#include "collie.h" + +struct cluster_cmd_data { + int copies; +} cluster_cmd_data; + +static int cluster_format(int argc, char **argv) +{ + int fd, ret; + struct sd_so_req hdr; + struct sd_so_rsp *rsp = (struct sd_so_rsp *)&hdr; + unsigned rlen, wlen; + struct timeval tv; + + fd = connect_to(sdhost, sdport); + if (fd < 0) + return EXIT_SYSFAIL; + + gettimeofday(&tv, NULL); + + memset(&hdr, 0, sizeof(hdr)); + + hdr.opcode = SD_OP_MAKE_FS; + hdr.copies = cluster_cmd_data.copies; + hdr.epoch = node_list_version; + hdr.ctime = (uint64_t) tv.tv_sec << 32 | tv.tv_usec * 1000; + + rlen = 0; + wlen = 0; + ret = exec_req(fd, (struct sd_req *)&hdr, NULL, &wlen, &rlen); + close(fd); + + if (ret) { + fprintf(stderr, "failed to connect\n"); + return EXIT_SYSFAIL; + } + + if (rsp->result != SD_RES_SUCCESS) { + fprintf(stderr, "%s\n", sd_strerror(rsp->result)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static int cluster_info(int argc, char **argv) +{ + int i, fd, ret; + struct sd_vdi_req hdr; + struct sd_vdi_rsp *rsp = (struct sd_vdi_rsp *)&hdr; + unsigned rlen, wlen; + struct epoch_log logs[8]; + int nr_logs; + time_t ti; + struct tm tm; + char time_str[128]; + + fd = connect_to(sdhost, sdport); + if (fd < 0) + return EXIT_SYSFAIL; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.opcode = SD_OP_STAT_CLUSTER; + hdr.epoch = node_list_version; + hdr.data_length = sizeof(logs); + + rlen = hdr.data_length; + wlen = 0; + ret = exec_req(fd, (struct sd_req *)&hdr, logs, &wlen, &rlen); + close(fd); + + if (ret != 0) + return EXIT_SYSFAIL; + + if (!raw_output) + printf("Cluster status: "); + if (rsp->result == SD_RES_SUCCESS) + printf("running\n"); + else + printf("%s\n", sd_strerror(rsp->result)); + + if (!raw_output) + printf("\nCreation time Epoch Nodes\n"); + + nr_logs = rsp->data_length / sizeof(struct epoch_log); + for (i = 0; i < nr_logs; i++) { + int j; + char name[128]; + struct sheepdog_node_list_entry *entry; + + ti = logs[i].ctime >> 32; + if (raw_output) { + snprintf(time_str, sizeof(time_str), "%" PRIu64, (uint64_t) ti); + } else { + localtime_r(&ti, &tm); + strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", &tm); + } + + printf(raw_output ? "%s %d" : "%s %6d", time_str, logs[i].epoch); + printf(" ["); + for (j = 0; j < logs[i].nr_nodes; j++) { + entry = logs[i].nodes + j; + printf("%s%s", + (j == 0) ? "" : ", ", + addr_to_str(name, sizeof(name), + entry->addr, entry->port)); + } + printf("]\n"); + } + + return EXIT_SUCCESS; +} + +static int cluster_shutdown(int argc, char **argv) +{ + int fd, ret; + struct sd_req hdr; + struct sd_rsp *rsp = (struct sd_rsp *)&hdr; + unsigned rlen, wlen; + + fd = connect_to(sdhost, sdport); + if (fd < 0) + return EXIT_SYSFAIL; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.opcode = SD_OP_SHUTDOWN; + hdr.epoch = node_list_version; + + rlen = 0; + wlen = 0; + ret = exec_req(fd, &hdr, NULL, &wlen, &rlen); + close(fd); + + if (ret) { + fprintf(stderr, "failed to connect\n"); + return EXIT_SYSFAIL; + } + + if (rsp->result != SD_RES_SUCCESS) { + fprintf(stderr, "%s\n", sd_strerror(rsp->result)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static struct subcommand cluster_cmd[] = { + {"info", NULL, "aprh", "show cluster information", + 0, cluster_info}, + {"format", NULL, "caph", "create a Sheepdog storage", + 0, cluster_format}, + {"shutdown", NULL, "aph", "stop Sheepdog", + SUBCMD_FLAG_NEED_NODELIST, cluster_shutdown}, + {NULL,}, +}; + +static int cluster_parser(int ch, char *opt) +{ + switch (ch) { + case 'c': + cluster_cmd_data.copies = atoi(opt); + break; + } + + return 0; +} + +struct command cluster_command = { + "cluster", + cluster_cmd, + cluster_parser +}; diff --git a/collie/collie.c b/collie/collie.c index c5eae56..e064a0a 100644 --- a/collie/collie.c +++ b/collie/collie.c @@ -8,45 +8,20 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <ctype.h> -#include <errno.h> #include <getopt.h> #include <inttypes.h> -#include <netdb.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <arpa/inet.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <time.h> -#include <sys/time.h> #include "sheepdog_proto.h" #include "sheep.h" -#include "net.h" -#include "treeview.h" -#include "exits.h" +#include "collie.h" static char program_name[] = "collie"; -static const char *sdhost = "localhost"; -static int sdport = SD_LISTEN_PORT; -static int highlight = 1; -static int raw_output = 0; - -#define TEXT_NORMAL "\033[0m" -#define TEXT_BOLD "\033[1m" - -struct sd_option { - int val; - const char *name; - int has_arg; - const char *desc; -}; +const char *sdhost = "localhost"; +int sdport = SD_LISTEN_PORT; +int highlight = 1; +int raw_output = 0; static const struct sd_option collie_options[] = { @@ -70,75 +45,14 @@ static const struct sd_option collie_options[] = { { 0, NULL, 0, NULL }, }; -static void usage(int status); - -static uint64_t node_list_version; - -static struct sheepdog_node_list_entry node_list_entries[SD_MAX_NODES]; -static struct sheepdog_vnode_list_entry vnode_list_entries[SD_MAX_VNODES]; -static int nr_nodes, nr_vnodes; -static unsigned master_idx; - -static int is_current(struct sheepdog_inode *i) -{ - return !i->snap_ctime; -} - -static char *size_to_str(uint64_t _size, char *str, int str_size) -{ - const char *units[] = {"MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; - int i = 0; - double size; - - if (raw_output) { - snprintf(str, str_size, "%" PRIu64, _size); - return str; - } - - size = (double)_size; - size /= 1024 * 1024; - while (i < ARRAY_SIZE(units) && size >= 1024) { - i++; - size /= 1024; - } - - if (size >= 10) - snprintf(str, str_size, "%.0lf %s", size, units[i]); - else - snprintf(str, str_size, "%.1lf %s", size, units[i]); - - return str; -} - -static int parse_option_size(const char *value, uint64_t *ret) -{ - char *postfix; - double sizef; +static void usage(struct command *commands, int status); - sizef = strtod(value, &postfix); - switch (*postfix) { - case 'T': - sizef *= 1024; - case 'G': - sizef *= 1024; - case 'M': - sizef *= 1024; - case 'K': - case 'k': - sizef *= 1024; - case 'b': - case '\0': - *ret = (uint64_t) sizef; - break; - default: - fprintf(stderr, "invalid parameter, %s\n", value); - fprintf(stderr, "You may use k, M, G or T suffixes for " - "kilobytes, megabytes, gigabytes and terabytes.\n"); - return -1; - } +uint64_t node_list_version; - return 0; -} +struct sheepdog_node_list_entry node_list_entries[SD_MAX_NODES]; +struct sheepdog_vnode_list_entry vnode_list_entries[SD_MAX_VNODES]; +int nr_nodes, nr_vnodes; +unsigned master_idx; static int update_node_list(int max_nodes, int epoch) { @@ -201,1446 +115,6 @@ out: return ret; } -static int sd_read_object(uint64_t oid, void *data, unsigned int datalen, - uint64_t offset) -{ - struct sd_obj_req hdr; - struct sd_obj_rsp *rsp = (struct sd_obj_rsp *)&hdr; - char name[128]; - int n, fd, ret; - unsigned wlen = 0, rlen = datalen; - - n = obj_to_sheep(vnode_list_entries, nr_vnodes, oid, 0); - - addr_to_str(name, sizeof(name), vnode_list_entries[n].addr, 0); - - fd = connect_to(name, vnode_list_entries[n].port); - if (fd < 0) { - fprintf(stderr, "failed to connect %s:%d\n", name, - vnode_list_entries[n].port); - return SD_RES_EIO; - } - - memset(&hdr, 0, sizeof(hdr)); - hdr.epoch = node_list_version; - hdr.opcode = SD_OP_READ_OBJ; - hdr.oid = oid; - /* use direct to avoid checking consistency */ - hdr.flags = SD_FLAG_CMD_DIRECT; - hdr.data_length = rlen; - hdr.offset = offset; - - ret = exec_req(fd, (struct sd_req *)&hdr, data, &wlen, &rlen); - close(fd); - - if (ret) { - fprintf(stderr, "failed to read object, %lx\n", oid); - return SD_RES_EIO; - } - - if (rsp->result != SD_RES_SUCCESS) { - fprintf(stderr, "failed to read object, %lx %s\n", oid, - sd_strerror(rsp->result)); - return rsp->result; - } - - return SD_RES_SUCCESS; -} - -static int sd_write_object(uint64_t oid, void *data, unsigned int datalen, - uint64_t offset, uint32_t flags, int copies, int create) -{ - struct sd_obj_req hdr; - struct sd_obj_rsp *rsp = (struct sd_obj_rsp *)&hdr; - int fd, ret; - unsigned wlen = datalen, rlen; - - fd = connect_to(sdhost, sdport); - if (fd < 0) { - fprintf(stderr, "failed to connect\n"); - return SD_RES_EIO; - } - - memset(&hdr, 0, sizeof(hdr)); - hdr.epoch = node_list_version; - if (create) - hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ; - else - hdr.opcode = SD_OP_WRITE_OBJ; - hdr.oid = oid; - hdr.copies = copies; - hdr.data_length = wlen; - hdr.flags = (flags & ~SD_FLAG_CMD_DIRECT) | SD_FLAG_CMD_WRITE; - hdr.offset = offset; - - ret = exec_req(fd, (struct sd_req *)&hdr, data, &wlen, &rlen); - close(fd); - - if (ret) { - fprintf(stderr, "failed to write object, %lx\n", oid); - return SD_RES_EIO; - } - if (rsp->result != SD_RES_SUCCESS) { - fprintf(stderr, "failed to write object, %lx %s\n", oid, - sd_strerror(rsp->result)); - return rsp->result; - } - - return SD_RES_SUCCESS; -} - -struct cluster_cmd_data { - int copies; -} cluster_cmd_data; - -struct vdi_cmd_data { - unsigned int index; - int snapshot_id; - char snapshot_tag[SD_MAX_VDI_TAG_LEN]; - int exclusive; - int delete; - int prealloc; -} vdi_cmd_data = { ~0, }; - -static int cluster_format(int argc, char **argv) -{ - int fd, ret; - struct sd_so_req hdr; - struct sd_so_rsp *rsp = (struct sd_so_rsp *)&hdr; - unsigned rlen, wlen; - struct timeval tv; - - fd = connect_to(sdhost, sdport); - if (fd < 0) - return EXIT_SYSFAIL; - - gettimeofday(&tv, NULL); - - memset(&hdr, 0, sizeof(hdr)); - - hdr.opcode = SD_OP_MAKE_FS; - hdr.copies = cluster_cmd_data.copies; - hdr.epoch = node_list_version; - hdr.ctime = (uint64_t) tv.tv_sec << 32 | tv.tv_usec * 1000; - - rlen = 0; - wlen = 0; - ret = exec_req(fd, (struct sd_req *)&hdr, NULL, &wlen, &rlen); - close(fd); - - if (ret) { - fprintf(stderr, "failed to connect\n"); - return EXIT_SYSFAIL; - } - - if (rsp->result != SD_RES_SUCCESS) { - fprintf(stderr, "%s\n", sd_strerror(rsp->result)); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -static int shutdown_sheepdog(void) -{ - int fd, ret; - struct sd_req hdr; - struct sd_rsp *rsp = (struct sd_rsp *)&hdr; - unsigned rlen, wlen; - - fd = connect_to(sdhost, sdport); - if (fd < 0) - return EXIT_SYSFAIL; - - memset(&hdr, 0, sizeof(hdr)); - - hdr.opcode = SD_OP_SHUTDOWN; - hdr.epoch = node_list_version; - - rlen = 0; - wlen = 0; - ret = exec_req(fd, &hdr, NULL, &wlen, &rlen); - close(fd); - - if (ret) { - fprintf(stderr, "failed to connect\n"); - return EXIT_SYSFAIL; - } - - if (rsp->result != SD_RES_SUCCESS) { - fprintf(stderr, "%s\n", sd_strerror(rsp->result)); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -typedef void (*vdi_parser_func_t)(uint32_t vid, char *name, char *tag, - uint32_t snapid, uint32_t flags, - struct sheepdog_inode *i, void *data); - -static int parse_vdi(vdi_parser_func_t func, size_t size, void *data) -{ - int ret, fd; - unsigned long nr; - static struct sheepdog_inode i; - struct sd_req req; - static DECLARE_BITMAP(vdi_inuse, SD_NR_VDIS); - unsigned int rlen, wlen = 0; - - fd = connect_to(sdhost, sdport); - if (fd < 0) - return fd; - - memset(&req, 0, sizeof(req)); - - req.opcode = SD_OP_READ_VDIS; - req.data_length = sizeof(vdi_inuse); - req.epoch = node_list_version; - - rlen = sizeof(vdi_inuse); - ret = exec_req(fd, &req, vdi_inuse, &wlen, &rlen); - if (ret < 0) { - close(fd); - return ret; - } - close(fd); - - for (nr = 0; nr < SD_NR_VDIS; nr++) { - uint64_t oid; - - if (!test_bit(nr, vdi_inuse)) - continue; - - oid = vid_to_vdi_oid(nr); - - memset(&i, 0, sizeof(i)); - ret = sd_read_object(oid, &i, SD_INODE_HEADER_SIZE, 0); - if (ret != SD_RES_SUCCESS) { - fprintf(stderr, "failed to read a inode header\n"); - continue; - } - - if (i.name[0] == '\0') /* this vdi is deleted */ - continue; - - if (size > SD_INODE_HEADER_SIZE) { - rlen = DIV_ROUND_UP(i.vdi_size, SD_DATA_OBJ_SIZE) * - sizeof(i.data_vdi_id[0]); - if (rlen > size - SD_INODE_HEADER_SIZE) - rlen = size - SD_INODE_HEADER_SIZE; - - ret = sd_read_object(oid, ((char *)&i) + SD_INODE_HEADER_SIZE, - rlen, SD_INODE_HEADER_SIZE); - - if (ret != SD_RES_SUCCESS) { - fprintf(stderr, "failed to read inode\n"); - continue; - } - } - - func(i.vdi_id, i.name, i.tag, i.snap_id, 0, &i, data); - } - - return 0; -} - -struct get_vdi_info { - char *name; - char *tag; - uint32_t vid; - uint32_t snapid; -}; - -static void print_vdi_list(uint32_t vid, char *name, char *tag, uint32_t snapid, - uint32_t flags, struct sheepdog_inode *i, void *data) -{ - int idx; - uint64_t my_objs, cow_objs; - char vdi_size_str[16], my_objs_str[16], cow_objs_str[16]; - time_t ti; - struct tm tm; - char dbuf[128]; - struct get_vdi_info *info = data; - - if (info && strcmp(name, info->name) != 0) - return; - - ti = i->ctime >> 32; - if (raw_output) { - snprintf(dbuf, sizeof(dbuf), "%" PRIu64, (uint64_t) ti); - } else { - localtime_r(&ti, &tm); - strftime(dbuf, sizeof(dbuf), - "%Y-%m-%d %H:%M", &tm); - } - - my_objs = 0; - cow_objs = 0; - for (idx = 0; idx < MAX_DATA_OBJS; idx++) { - if (!i->data_vdi_id[idx]) - continue; - if (is_data_obj_writeable(i, idx)) - my_objs++; - else - cow_objs++; - } - - size_to_str(i->vdi_size, vdi_size_str, sizeof(vdi_size_str)); - size_to_str(my_objs * SD_DATA_OBJ_SIZE, my_objs_str, sizeof(my_objs_str)); - size_to_str(cow_objs * SD_DATA_OBJ_SIZE, cow_objs_str, sizeof(cow_objs_str)); - - if (raw_output) { - printf("%c ", is_current(i) ? '=' : 's'); - while (*name) { - if (isspace(*name) || *name == '\\') - putchar('\\'); - putchar(*name++); - } - printf(" %d %s %s %s %s %" PRIx32 "\n", snapid, - vdi_size_str, my_objs_str, cow_objs_str, dbuf, vid); - } else { - printf("%c %-8s %5d %7s %7s %7s %s %7" PRIx32 "\n", - is_current(i) ? ' ' : 's', name, snapid, - vdi_size_str, my_objs_str, cow_objs_str, dbuf, vid); - } -} - -static void print_vdi_tree(uint32_t vid, char *name, char * tag, uint32_t snapid, - uint32_t flags, struct sheepdog_inode *i, void *data) -{ - time_t ti; - struct tm tm; - char buf[128]; - - if (is_current(i)) - strcpy(buf, "(You Are Here)"); - else { - ti = i->ctime >> 32; - localtime_r(&ti, &tm); - - strftime(buf, sizeof(buf), - "[%Y-%m-%d %H:%M]", &tm); - } - - add_vdi_tree(name, buf, vid, i->parent_vdi_id, highlight && is_current(i)); -} - -static void print_vdi_graph(uint32_t vid, char *name, char * tag, uint32_t snapid, - uint32_t flags, struct sheepdog_inode *i, void *data) -{ - time_t ti; - struct tm tm; - char dbuf[128], tbuf[128], size_str[128]; - - ti = i->ctime >> 32; - localtime_r(&ti, &tm); - - strftime(dbuf, sizeof(dbuf), "%Y-%m-%d", &tm); - strftime(tbuf, sizeof(tbuf), "%H:%M:%S", &tm); - size_to_str(i->vdi_size, size_str, sizeof(size_str)); - - printf(" \"%x\" -> \"%x\";\n", i->parent_vdi_id, vid); - printf(" \"%x\" [\n" - " group = \"%s\",\n" - " label = \"", - vid, name); - printf("name: %10s\\n" - "tag : %10x\\n" - "size: %10s\\n" - "date: %10s\\n" - "time: %10s", - name, snapid, size_str, dbuf, tbuf); - - if (is_current(i)) - printf("\",\n color=\"red\"\n ];\n\n"); - else - printf("\"\n ];\n\n"); - -} - -static void cal_total_vdi_size(uint32_t vid, char *name, char * tag, - uint32_t snapid, uint32_t flags, - struct sheepdog_inode *i, void *data) -{ - uint64_t *size = data; - - if (is_current(i)) - *size += i->vdi_size; -} - -static void get_oid(uint32_t vid, char *name, char *tag, uint32_t snapid, - uint32_t flags, struct sheepdog_inode *i, void *data) -{ - struct get_vdi_info *info = data; - - if (info->name) { - if (info->tag) { - if (!strcmp(name, info->name) && !strcmp(tag, info->tag)) - info->vid = vid; - } else if (info->snapid) { - if (!strcmp(name, info->name) && snapid == info->snapid) - info->vid = vid; - } else { - if (!strcmp(name, info->name)) - info->vid = vid; - } - } -} - -typedef void (*obj_parser_func_t)(char *sheep, uint64_t oid, - struct sd_obj_rsp *rsp, char *buf, void *data); - -static void do_print_obj(char *sheep, uint64_t oid, struct sd_obj_rsp *rsp, - char *buf, void *data) -{ - switch (rsp->result) { - case SD_RES_SUCCESS: - printf("%s: has the object (should be %d copies)\n", - sheep, rsp->copies); - break; - case SD_RES_NO_OBJ: - printf("%s: doesn't have\n", sheep); - break; - case SD_RES_OLD_NODE_VER: - case SD_RES_NEW_NODE_VER: - printf("the node list has changed, try again\n"); - break; - default: - printf("%s: hit an expected error, %d\n", - sheep, rsp->result); - break; - } -} - -struct get_data_oid_info { - int success; - uint64_t data_oid; - unsigned idx; -}; - -static void get_data_oid(char *sheep, uint64_t oid, struct sd_obj_rsp *rsp, - char *buf, void *data) -{ - struct get_data_oid_info *info = data; - struct sheepdog_inode *inode = (struct sheepdog_inode *)buf; - - switch (rsp->result) { - case SD_RES_SUCCESS: - if (info->success) - break; - info->success = 1; - if (inode->data_vdi_id[info->idx]) - info->data_oid = vid_to_data_oid(inode->data_vdi_id[info->idx], info->idx); - break; - case SD_RES_NO_OBJ: - break; - case SD_RES_OLD_NODE_VER: - case SD_RES_NEW_NODE_VER: - printf("the node list has changed, try again\n"); - break; - default: - printf("%s: hit an expected error, %d\n", - sheep, rsp->result); - break; - } -} - -static void parse_objs(uint64_t oid, obj_parser_func_t func, void *data) -{ - char name[128]; - int i, fd, ret; - char *buf; - - buf = zalloc(sizeof(struct sheepdog_inode)); - if (!buf) { - fprintf(stderr, "out of memory\n"); - return; - } - - for (i = 0; i < nr_nodes; i++) { - unsigned wlen = 0, rlen = sizeof(struct sheepdog_inode); - struct sd_obj_req hdr; - struct sd_obj_rsp *rsp = (struct sd_obj_rsp *)&hdr; - - addr_to_str(name, sizeof(name), node_list_entries[i].addr, 0); - - fd = connect_to(name, node_list_entries[i].port); - if (fd < 0) - break; - - memset(&hdr, 0, sizeof(hdr)); - - hdr.opcode = SD_OP_READ_OBJ; - hdr.data_length = rlen; - hdr.flags = SD_FLAG_CMD_DIRECT; - hdr.oid = oid; - hdr.epoch = node_list_version; - - ret = exec_req(fd, (struct sd_req *)&hdr, buf, &wlen, &rlen); - close(fd); - - sprintf(name + strlen(name), ":%d", node_list_entries[i].port); - - if (ret) - printf("%s: can't connect\n", name); - else - func(name, oid, rsp, buf, data); - } - - free(buf); -} - -#define SUBCMD_FLAG_NEED_NODELIST (1 << 0) -#define SUBCMD_FLAG_NEED_THIRD_ARG (1 << 1) - -struct subcommand { - const char *name; - const char *arg; - const char *opts; - const char *desc; - unsigned long flags; - int (*fn)(int, char **); -}; - -static int node_list(int argc, char **argv) -{ - int i; - - if (!raw_output) { - printf(" Idx - Host:Port Vnodes Zone\n"); - printf("---------------------------------------------\n"); - } - for (i = 0; i < nr_nodes; i++) { - char data[128]; - - addr_to_str(data, sizeof(data), node_list_entries[i].addr, - node_list_entries[i].port); - - if (i == master_idx) { - if (highlight) - printf(TEXT_BOLD); - printf(raw_output ? "* %d %s %d %d\n" : "* %4d - %-20s\t%d%11d\n", - i, data, node_list_entries[i].nr_vnodes, - node_list_entries[i].zone); - if (highlight) - printf(TEXT_NORMAL); - } else - printf(raw_output ? "- %d %s %d %d\n" : " %4d - %-20s\t%d%11d\n", - i, data, node_list_entries[i].nr_vnodes, - node_list_entries[i].zone); - } - - return EXIT_SUCCESS; -} - -static int node_info(int argc, char **argv) -{ - int i, ret, success = 0; - uint64_t total_size = 0, total_avail = 0, total_vdi_size = 0; - char total_str[8], avail_str[8], vdi_size_str[8]; - - if (!raw_output) - printf("Id\tSize\tUsed\tUse%%\n"); - - for (i = 0; i < nr_nodes; i++) { - char name[128]; - int fd; - unsigned wlen, rlen; - struct sd_node_req req; - struct sd_node_rsp *rsp = (struct sd_node_rsp *)&req; - char store_str[8], free_str[8]; - - addr_to_str(name, sizeof(name), node_list_entries[i].addr, 0); - - fd = connect_to(name, node_list_entries[i].port); - if (fd < 0) - return 1; - - memset(&req, 0, sizeof(req)); - - req.opcode = SD_OP_STAT_SHEEP; - req.epoch = node_list_version; - - wlen = 0; - rlen = 0; - ret = exec_req(fd, (struct sd_req *)&req, NULL, &wlen, &rlen); - close(fd); - - size_to_str(rsp->store_size, store_str, sizeof(store_str)); - size_to_str(rsp->store_size - rsp->store_free, free_str, - sizeof(free_str)); - if (!ret && rsp->result == SD_RES_SUCCESS) { - printf(raw_output ? "%d %s %s %d%%\n" : "%2d\t%s\t%s\t%3d%%\n", - i, store_str, free_str, - (int)(((double)(rsp->store_size - rsp->store_free) / rsp->store_size) * 100)); - success++; - } - - total_size += rsp->store_size; - total_avail += rsp->store_free; - } - - if (success == 0) { - fprintf(stderr, "cannot get information from any nodes\n"); - return EXIT_SYSFAIL; - } - - parse_vdi(cal_total_vdi_size, SD_INODE_HEADER_SIZE, &total_vdi_size); - - size_to_str(total_size, total_str, sizeof(total_str)); - size_to_str(total_size - total_avail, avail_str, sizeof(avail_str)); - size_to_str(total_vdi_size, vdi_size_str, sizeof(vdi_size_str)); - printf(raw_output ? "Total %s %s %d%% %s\n" - : "\nTotal\t%s\t%s\t%3d%%, total virtual VDI Size\t%s\n", - total_str, avail_str, - (int)(((double)(total_size - total_avail) / total_size) * 100), - vdi_size_str); - - return EXIT_SUCCESS; -} - -static struct subcommand node_cmd[] = { - {"list", NULL, "aprh", "list nodes", - SUBCMD_FLAG_NEED_NODELIST, node_list}, - {"info", NULL, "aprh", "show each node information", - SUBCMD_FLAG_NEED_NODELIST, node_info}, - {NULL,}, -}; - -static int vdi_list(int argc, char **argv) -{ - char *vdiname = argv[optind]; - - if (!raw_output) { - printf(" name id size used shared creation time vdi id\n"); - printf("------------------------------------------------------------------\n"); - } - - if (vdiname) { - struct get_vdi_info info; - memset(&info, 0, sizeof(info)); - info.name = vdiname; - parse_vdi(print_vdi_list, SD_INODE_SIZE, &info); - return EXIT_SUCCESS; - } else { - parse_vdi(print_vdi_list, SD_INODE_SIZE, NULL); - return EXIT_SUCCESS; - } -} - -static int vdi_tree(int argc, char **argv) -{ - init_tree(); - parse_vdi(print_vdi_tree, SD_INODE_HEADER_SIZE, NULL); - dump_tree(); - - return EXIT_SUCCESS; -} - -static int vdi_graph(int argc, char **argv) -{ - /* print a header */ - printf("digraph G {\n"); - printf(" node [shape = \"box\", fontname = \"Courier\"];\n\n"); - printf(" \"0\" [shape = \"ellipse\", label = \"root\"];\n\n"); - - parse_vdi(print_vdi_graph, SD_INODE_HEADER_SIZE, NULL); - - /* print a footer */ - printf("}\n"); - - return EXIT_SUCCESS; -} - -static int find_vdi_name(char *vdiname, uint32_t snapid, const char *tag, - uint32_t *vid, int for_snapshot) -{ - int ret, fd; - struct sd_vdi_req hdr; - struct sd_vdi_rsp *rsp = (struct sd_vdi_rsp *)&hdr; - unsigned int wlen, rlen = 0; - char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN]; - - fd = connect_to(sdhost, sdport); - if (fd < 0) - return -1; - - memset(buf, 0, sizeof(buf)); - strncpy(buf, vdiname, SD_MAX_VDI_LEN); - strncpy(buf + SD_MAX_VDI_LEN, tag, SD_MAX_VDI_TAG_LEN); - - memset(&hdr, 0, sizeof(hdr)); - if (for_snapshot) - hdr.opcode = SD_OP_GET_VDI_INFO; - else - hdr.opcode = SD_OP_LOCK_VDI; - wlen = SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN; - hdr.proto_ver = SD_PROTO_VER; - hdr.data_length = wlen; - hdr.snapid = snapid; - hdr.flags = SD_FLAG_CMD_WRITE; - - ret = exec_req(fd, (struct sd_req *)&hdr, buf, &wlen, &rlen); - if (ret) { - ret = -1; - goto out; - } - - if (rsp->result != SD_RES_SUCCESS) { - fprintf(stderr, "cannot get vdi info, %s, %s %d %s\n", - sd_strerror(rsp->result), vdiname, snapid, tag); - ret = -1; - goto out; - } - *vid = rsp->vdi_id; - - ret = 0; -out: - close(fd); - return ret; -} - -static int do_vdi_create(char *vdiname, int64_t vdi_size, uint32_t base_vid, - uint32_t *vdi_id, int snapshot) -{ - struct sd_vdi_req hdr; - struct sd_vdi_rsp *rsp = (struct sd_vdi_rsp *)&hdr; - int fd, ret; - unsigned int wlen, rlen = 0; - char buf[SD_MAX_VDI_LEN]; - - fd = connect_to(sdhost, sdport); - if (fd < 0) { - fprintf(stderr, "failed to connect\n"); - return EXIT_SYSFAIL; - } - - memset(buf, 0, sizeof(buf)); - strncpy(buf, vdiname, SD_MAX_VDI_LEN); - - memset(&hdr, 0, sizeof(hdr)); - hdr.opcode = SD_OP_NEW_VDI; - hdr.base_vdi_id = base_vid; - - wlen = SD_MAX_VDI_LEN; - - hdr.flags = SD_FLAG_CMD_WRITE; - hdr.snapid = snapshot; - - hdr.data_length = wlen; - hdr.vdi_size = vdi_size; - - ret = exec_req(fd, (struct sd_req *)&hdr, buf, &wlen, &rlen); - - close(fd); - - if (ret) { - fprintf(stderr, "failed to send a request\n"); - return EXIT_SYSFAIL; - } - - if (rsp->result != SD_RES_SUCCESS) { - fprintf(stderr, "%s, %s\n", sd_strerror(rsp->result), vdiname); - return EXIT_FAILURE; - } - - if (vdi_id) - *vdi_id = rsp->vdi_id; - - return EXIT_SUCCESS; -} - -static int vdi_create(int argc, char **argv) -{ - char *vdiname = argv[optind++]; - uint64_t size; - uint32_t vid; - uint64_t oid; - int idx, max_idx, ret; - struct sheepdog_inode *inode = NULL; - char *buf = NULL; - - if (!argv[optind]) { - fprintf(stderr, "please specify the size of vdi\n"); - return EXIT_USAGE; - } - ret = parse_option_size(argv[optind], &size); - if (ret < 0) - return EXIT_USAGE; - if (size > SD_MAX_VDI_SIZE) { - fprintf(stderr, "too big image size, %s\n", argv[optind]); - return EXIT_USAGE; - } - - ret = do_vdi_create(vdiname, size, 0, &vid, 0); - if (ret != EXIT_SUCCESS || !vdi_cmd_data.prealloc) - goto out; - - inode = malloc(sizeof(*inode)); - buf = zalloc(SD_DATA_OBJ_SIZE); - if (!inode || !buf) { - fprintf(stderr, "oom\n"); - ret = EXIT_SYSFAIL; - goto out; - } - - ret = sd_read_object(vid_to_vdi_oid(vid), inode, sizeof(*inode), 0); - if (ret != SD_RES_SUCCESS) { - fprintf(stderr, "failed to read a newly created vdi object\n"); - ret = EXIT_FAILURE; - goto out; - } - max_idx = DIV_ROUND_UP(size, SD_DATA_OBJ_SIZE); - - for (idx = 0; idx < max_idx; idx++) { - oid = vid_to_data_oid(vid, idx); - - ret = sd_write_object(oid, buf, SD_DATA_OBJ_SIZE, 0, 0, - inode->nr_copies, 1); - if (ret != SD_RES_SUCCESS) { - ret = EXIT_FAILURE; - goto out; - } - - inode->data_vdi_id[idx] = vid; - ret = sd_write_object(vid_to_vdi_oid(vid), &vid, sizeof(vid), - SD_INODE_HEADER_SIZE + sizeof(vid) * idx, 0, - inode->nr_copies, 0); - if (ret) { - ret = EXIT_FAILURE; - goto out; - } - } - ret = EXIT_SUCCESS; -out: - free(inode); - free(buf); - return ret; -} - -static int vdi_snapshot(int argc, char **argv) -{ - char *vdiname = argv[optind++]; - uint32_t vid; - int ret; - char buf[SD_INODE_HEADER_SIZE]; - struct sheepdog_inode *inode = (struct sheepdog_inode *)buf; - - if (vdi_cmd_data.snapshot_id != 0) { - fprintf(stderr, "please specify a non-integer value for " - "a snapshot tag name\n"); - return EXIT_USAGE; - } - - ret = find_vdi_name(vdiname, 0, "", &vid, 0); - if (ret < 0) { - fprintf(stderr, "failed to open vdi %s\n", vdiname); - return EXIT_FAILURE; - } - - ret = sd_read_object(vid_to_vdi_oid(vid), inode, SD_INODE_HEADER_SIZE, 0); - if (ret != SD_RES_SUCCESS) { - fprintf(stderr, "failed to read an inode header\n"); - return EXIT_FAILURE; - } - - if (vdi_cmd_data.snapshot_tag[0]) { - ret = sd_write_object(vid_to_vdi_oid(vid), vdi_cmd_data.snapshot_tag, - SD_MAX_VDI_TAG_LEN, - offsetof(struct sheepdog_inode, tag), - 0, inode->nr_copies, 0); - } - - return do_vdi_create(vdiname, inode->vdi_size, vid, NULL, 1); -} - -static int vdi_clone(int argc, char **argv) -{ - char *src_vdi = argv[optind++], *dst_vdi; - uint32_t base_vid, new_vid; - uint64_t oid; - int idx, max_idx, ret; - struct sheepdog_inode *inode = NULL; - char *buf = NULL; - - dst_vdi = argv[optind]; - if (!dst_vdi) { - fprintf(stderr, "a dst vdi must be specified\n"); - ret = EXIT_USAGE; - goto out; - } - - if (!vdi_cmd_data.snapshot_id && !vdi_cmd_data.snapshot_tag[0]) { - fprintf(stderr, "it is not supported to create a clone image of " - "the non-snapshot vdi\n"); - fprintf(stderr, "please specify a '-s' option\n"); - ret = EXIT_USAGE; - goto out; - } - - ret = find_vdi_name(src_vdi, vdi_cmd_data.snapshot_id, - vdi_cmd_data.snapshot_tag, &base_vid, 0); - if (ret < 0) { - fprintf(stderr, "failed to open vdi %s\n", src_vdi); - ret = EXIT_FAILURE; - goto out; - } - - inode = malloc(sizeof(*inode)); - if (!inode) { - fprintf(stderr, "oom\n"); - ret = EXIT_SYSFAIL; - goto out; - } - ret = sd_read_object(vid_to_vdi_oid(base_vid), inode, SD_INODE_SIZE, 0); - if (ret != SD_RES_SUCCESS) { - fprintf(stderr, "failed to read a base inode\n"); - ret = EXIT_FAILURE; - goto out; - } - - ret = do_vdi_create(dst_vdi, inode->vdi_size, base_vid, &new_vid, 0); - if (ret != EXIT_SUCCESS || !vdi_cmd_data.prealloc) - goto out; - - buf = zalloc(SD_DATA_OBJ_SIZE); - if (!buf) { - fprintf(stderr, "oom\n"); - ret = EXIT_SYSFAIL; - goto out; - } - - max_idx = DIV_ROUND_UP(inode->vdi_size, SD_DATA_OBJ_SIZE); - - for (idx = 0; idx < max_idx; idx++) { - if (inode->data_vdi_id[idx]) { - oid = vid_to_data_oid(inode->data_vdi_id[idx], idx); - ret = sd_read_object(oid, buf, SD_DATA_OBJ_SIZE, 0); - if (ret) { - ret = EXIT_FAILURE; - goto out; - } - } else - memset(buf, 0, SD_DATA_OBJ_SIZE); - - oid = vid_to_data_oid(new_vid, idx); - ret = sd_write_object(oid, buf, SD_DATA_OBJ_SIZE, 0, 0, - inode->nr_copies, 1); - if (ret != SD_RES_SUCCESS) { - ret = EXIT_FAILURE; - goto out; - } - - ret = sd_write_object(vid_to_vdi_oid(new_vid), &new_vid, sizeof(new_vid), - SD_INODE_HEADER_SIZE + sizeof(new_vid) * idx, 0, - inode->nr_copies, 0); - if (ret) { - ret = EXIT_FAILURE; - goto out; - } - } - ret = EXIT_SUCCESS; -out: - free(inode); - free(buf); - return ret; -} - -static int vdi_resize(int argc, char **argv) -{ - char *vdiname = argv[optind++]; - uint64_t new_size; - uint32_t vid; - int ret; - char buf[SD_INODE_HEADER_SIZE]; - struct sheepdog_inode *inode = (struct sheepdog_inode *)buf; - - if (!argv[optind]) { - fprintf(stderr, "please specify a new size of vdi\n"); - return EXIT_USAGE; - } - ret = parse_option_size(argv[optind], &new_size); - if (ret < 0) - return EXIT_USAGE; - if (new_size > SD_MAX_VDI_SIZE) { - fprintf(stderr, "too big image size, %s\n", argv[optind]); - return EXIT_USAGE; - } - - ret = find_vdi_name(vdiname, 0, "", &vid, 0); - if (ret < 0) { - fprintf(stderr, "failed to open vdi %s\n", vdiname); - return EXIT_FAILURE; - } - - ret = sd_read_object(vid_to_vdi_oid(vid), inode, SD_INODE_HEADER_SIZE, 0); - if (ret != SD_RES_SUCCESS) { - fprintf(stderr, "failed to read an inode header\n"); - return EXIT_FAILURE; - } - - if (new_size < inode->vdi_size) { - fprintf(stderr, "shrinking is not implemented\n"); - return EXIT_USAGE; - } - inode->vdi_size = new_size; - - ret = sd_write_object(vid_to_vdi_oid(vid), inode, SD_INODE_HEADER_SIZE, 0, - 0, inode->nr_copies, 0); - if (ret != SD_RES_SUCCESS) { - fprintf(stderr, "failed to update an inode header\n"); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -static int vdi_delete(int argc, char **argv) -{ - char *data = argv[optind]; - int fd, ret; - struct sd_vdi_req hdr; - struct sd_vdi_rsp *rsp = (struct sd_vdi_rsp *)&hdr; - unsigned rlen, wlen; - char vdiname[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN]; - - fd = connect_to(sdhost, sdport); - if (fd < 0) - return EXIT_SYSFAIL; - - memset(&hdr, 0, sizeof(hdr)); - - rlen = 0; - wlen = sizeof(vdiname); - - hdr.opcode = SD_OP_DEL_VDI; - hdr.snapid = vdi_cmd_data.snapshot_id; - hdr.epoch = node_list_version; - hdr.flags = SD_FLAG_CMD_WRITE; - hdr.data_length = wlen; - memset(vdiname, 0, sizeof(vdiname)); - strncpy(vdiname, data, SD_MAX_VDI_LEN); - strncpy(vdiname + SD_MAX_VDI_LEN, vdi_cmd_data.snapshot_tag, - SD_MAX_VDI_TAG_LEN); - - ret = exec_req(fd, (struct sd_req *)&hdr, vdiname, &wlen, &rlen); - close(fd); - - if (ret) { - fprintf(stderr, "failed to connect\n"); - return EXIT_SYSFAIL; - } - - if (rsp->result != SD_RES_SUCCESS) { - fprintf(stderr, "%s: %s\n", vdiname, sd_strerror(rsp->result)); - if (rsp->result == SD_RES_NO_VDI) - return EXIT_MISSING; - else - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -static int vdi_object(int argc, char **argv) -{ - char *vdiname = argv[optind]; - unsigned idx = vdi_cmd_data.index; - int ret; - struct get_vdi_info info; - uint32_t vid; - - memset(&info, 0, sizeof(info)); - info.name = vdiname; - info.tag = vdi_cmd_data.snapshot_tag; - info.vid = 0; - info.snapid = vdi_cmd_data.snapshot_id; - - ret = parse_vdi(get_oid, SD_INODE_HEADER_SIZE, &info); - - vid = info.vid; - if (vid == 0) { - printf("No such vdi\n"); - return EXIT_MISSING; - } - - if (idx == ~0) { - printf("Looking for the inode object 0x%" PRIx32 " with %d nodes\n\n", - vid, nr_nodes); - parse_objs(vid_to_vdi_oid(vid), do_print_obj, NULL); - } else { - struct get_data_oid_info old_info; - - old_info.success = 0; - old_info.idx = idx; - - if (idx >= MAX_DATA_OBJS) { - printf("The offset is too large!\n"); - exit(EXIT_FAILURE); - } - - parse_objs(vid_to_vdi_oid(vid), get_data_oid, &old_info); - - if (old_info.success) { - if (old_info.data_oid) { - printf("Looking for the object 0x%" PRIx64 - " (the inode vid 0x%" PRIx32 " idx %u) with %d nodes\n\n", - old_info.data_oid, vid, idx, nr_nodes); - - parse_objs(old_info.data_oid, do_print_obj, NULL); - } else - printf("The inode object 0x%" PRIx32 " idx %u is not allocated\n", - vid, idx); - } else - printf("failed to read the inode object 0x%" PRIx32 "\n", vid); - } - - return EXIT_SUCCESS; -} - -static int find_vdi_attr_oid(char *vdiname, char *tag, uint32_t snapid, - char *key, uint32_t *vid, uint64_t *oid, - unsigned int *nr_copies, int creat, int excl) -{ - struct sd_vdi_req hdr; - struct sd_vdi_rsp *rsp = (struct sd_vdi_rsp *)&hdr; - int fd, ret; - unsigned int wlen, rlen; - char buf[SD_ATTR_HEADER_SIZE]; - - memset(buf, 0, sizeof(buf)); - strncpy(buf, vdiname, SD_MAX_VDI_LEN); - strncpy(buf + SD_MAX_VDI_LEN, vdi_cmd_data.snapshot_tag, - SD_MAX_VDI_TAG_LEN); - memcpy(buf + SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN, - &vdi_cmd_data.snapshot_id, sizeof(uint32_t)); - strncpy(buf + SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN + sizeof(uint32_t), - key, SD_MAX_VDI_ATTR_KEY_LEN); - - fd = connect_to(sdhost, sdport); - if (fd < 0) { - fprintf(stderr, "failed to connect\n\n"); - return SD_RES_EIO; - } - - memset(&hdr, 0, sizeof(hdr)); - hdr.opcode = SD_OP_GET_VDI_ATTR; - wlen = SD_ATTR_HEADER_SIZE; - rlen = 0; - hdr.proto_ver = SD_PROTO_VER; - hdr.data_length = wlen; - hdr.snapid = vdi_cmd_data.snapshot_id; - hdr.flags = SD_FLAG_CMD_WRITE; - if (creat) - hdr.flags |= SD_FLAG_CMD_CREAT; - if (excl) - hdr.flags |= SD_FLAG_CMD_EXCL; - - ret = exec_req(fd, (struct sd_req *)&hdr, buf, &wlen, &rlen); - if (ret) { - ret = SD_RES_EIO; - goto out; - } - - if (rsp->result != SD_RES_SUCCESS) { - ret = rsp->result; - goto out; - } - - *vid = rsp->vdi_id; - *oid = vid_to_attr_oid(rsp->vdi_id, rsp->attr_id); - *nr_copies = rsp->copies; - - ret = SD_RES_SUCCESS; -out: - close(fd); - return ret; -} - -static int vdi_setattr(int argc, char **argv) -{ - int ret; - uint64_t oid, attr_oid = 0; - uint32_t vid = 0, nr_copies = 0; - char *vdiname = argv[optind++], *key, *value; - uint64_t offset; - - key = argv[optind++]; - if (!key) { - fprintf(stderr, "please specify the name of key\n"); - return EXIT_USAGE; - } - - value = argv[optind++]; - if (!value && !vdi_cmd_data.delete) { - value = malloc(SD_MAX_VDI_ATTR_VALUE_LEN); - if (!value) { - fprintf(stderr, "failed to allocate memory\n"); - return EXIT_SYSFAIL; - } - - offset = 0; -reread: - ret = read(STDIN_FILENO, value + offset, - SD_MAX_VDI_ATTR_VALUE_LEN - offset); - if (ret < 0) { - fprintf(stderr, "failed to read from stdin, %m\n"); - return EXIT_SYSFAIL; - } - if (ret > 0) { - offset += ret; - goto reread; - } - } - - ret = find_vdi_attr_oid(vdiname, vdi_cmd_data.snapshot_tag, - vdi_cmd_data.snapshot_id, key, &vid, &attr_oid, - &nr_copies, !vdi_cmd_data.delete, - vdi_cmd_data.exclusive); - if (ret) { - if (ret == SD_RES_VDI_EXIST) { - fprintf(stderr, "the attribute already exists, %s\n", key); - return EXIT_EXISTS; - } else if (ret == SD_RES_NO_OBJ) { - fprintf(stderr, "no such attribute, %s\n", key); - return EXIT_MISSING; - } else if (ret == SD_RES_NO_VDI) { - fprintf(stderr, "vdi not found\n"); - return EXIT_MISSING; - } else - fprintf(stderr, "failed to find attr oid, %s\n", - sd_strerror(ret)); - return EXIT_FAILURE; - } - - oid = attr_oid; - - if (vdi_cmd_data.delete) - ret = sd_write_object(oid, (char *)"", 1, - offsetof(struct sheepdog_inode, name), 0, - nr_copies, 0); - else - ret = sd_write_object(oid, value, strlen(value), - SD_ATTR_HEADER_SIZE, SD_FLAG_CMD_TRUNCATE, - nr_copies, 0); - - if (ret != SD_RES_SUCCESS) { - fprintf(stderr, "failed to set attribute\n"); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} - -static int vdi_getattr(int argc, char **argv) -{ - struct sd_obj_req hdr; - struct sd_obj_rsp *rsp = (struct sd_obj_rsp *)&hdr; - int ret; - uint64_t oid, attr_oid = 0; - uint32_t vid = 0, nr_copies = 0; - char *vdiname = argv[optind++], *key, *value; - - key = argv[optind++]; - if (!key) { - fprintf(stderr, "please specify the name of key\n"); - return EXIT_USAGE; - } - - ret = find_vdi_attr_oid(vdiname, vdi_cmd_data.snapshot_tag, - vdi_cmd_data.snapshot_id, key, &vid, &attr_oid, - &nr_copies, 0, 0); - if (ret == SD_RES_NO_OBJ) { - fprintf(stderr, "no such attribute, %s\n", key); - return EXIT_MISSING; - } else if (ret == SD_RES_NO_VDI) { - fprintf(stderr, "vdi not found\n"); - return EXIT_MISSING; - } else if (ret) { - fprintf(stderr, "failed to find attr oid, %s\n", - sd_strerror(ret)); - return EXIT_MISSING; - } - - oid = attr_oid; - value = malloc(SD_MAX_VDI_ATTR_VALUE_LEN); - if (!value) { - fprintf(stderr, "failed to allocate memory\n"); - return EXIT_SYSFAIL; - } - - ret = sd_read_object(oid, value, SD_MAX_VDI_ATTR_VALUE_LEN, - SD_ATTR_HEADER_SIZE); - if (rsp->result == SD_RES_SUCCESS) { - printf("%s", value); - free(value); - return EXIT_SUCCESS; - } - - free(value); - return EXIT_FAILURE; -} - -static struct subcommand vdi_cmd[] = { - {"create", "<vdiname> <size>", "Paph", "create a image", - SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_create}, - {"snapshot", "<vdiname>", "saph", "create a snapshot", - SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_snapshot}, - {"clone", "<src vdi> <dst vdi>", "sPaph", "create a clone image", - SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_clone}, - {"delete", "<vdiname>", "saph", "delete a image", - SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_delete}, - {"list", "[vdiname]", "aprh", "list images", - SUBCMD_FLAG_NEED_NODELIST, vdi_list}, - {"tree", NULL, "aph", "show images in tree view format", - SUBCMD_FLAG_NEED_NODELIST, vdi_tree}, - {"graph", NULL, "aph", "show images with Graphviz dot format", - SUBCMD_FLAG_NEED_NODELIST, vdi_graph}, - {"object", "<vdiname>", "isaph", "show object information in the image", - SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_object}, - {"setattr", "<vdiname> <key> [value]", "dxaph", "set a vdi attribute", - SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_setattr}, - {"getattr", "<vdiname> <key>", "aph", "get a vdi attribute", - SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_getattr}, - {"resize", "<vdiname> <new size>", "aph", "resize a image", - SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_resize}, - {NULL,}, -}; - -static int vdi_parser(int ch, char *opt) -{ - switch (ch) { - case 'P': - vdi_cmd_data.prealloc = 1; - break; - case 'i': - vdi_cmd_data.index = atoi(opt); - break; - case 's': - vdi_cmd_data.snapshot_id = atoi(opt); - if (vdi_cmd_data.snapshot_id == 0) - strncpy(vdi_cmd_data.snapshot_tag, opt, - sizeof(vdi_cmd_data.snapshot_tag)); - break; - case 'x': - vdi_cmd_data.exclusive = 1; - break; - case 'd': - vdi_cmd_data.delete = 1; - break; - } - - return 0; -} - -static int cluster_info(int argc, char **argv) -{ - int i, fd, ret; - struct sd_vdi_req hdr; - struct sd_vdi_rsp *rsp = (struct sd_vdi_rsp *)&hdr; - unsigned rlen, wlen; - struct epoch_log logs[8]; - int nr_logs; - time_t ti; - struct tm tm; - char time_str[128]; - - fd = connect_to(sdhost, sdport); - if (fd < 0) - return EXIT_SYSFAIL; - - memset(&hdr, 0, sizeof(hdr)); - - hdr.opcode = SD_OP_STAT_CLUSTER; - hdr.epoch = node_list_version; - hdr.data_length = sizeof(logs); - - rlen = hdr.data_length; - wlen = 0; - ret = exec_req(fd, (struct sd_req *)&hdr, logs, &wlen, &rlen); - close(fd); - - if (ret != 0) - return EXIT_SYSFAIL; - - if (!raw_output) - printf("Cluster status: "); - if (rsp->result == SD_RES_SUCCESS) - printf("running\n"); - else - printf("%s\n", sd_strerror(rsp->result)); - - if (!raw_output) - printf("\nCreation time Epoch Nodes\n"); - - nr_logs = rsp->data_length / sizeof(struct epoch_log); - for (i = 0; i < nr_logs; i++) { - int j; - char name[128]; - struct sheepdog_node_list_entry *entry; - - ti = logs[i].ctime >> 32; - if (raw_output) { - snprintf(time_str, sizeof(time_str), "%" PRIu64, (uint64_t) ti); - } else { - localtime_r(&ti, &tm); - strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", &tm); - } - - printf(raw_output ? "%s %d" : "%s %6d", time_str, logs[i].epoch); - printf(" ["); - for (j = 0; j < logs[i].nr_nodes; j++) { - entry = logs[i].nodes + j; - printf("%s%s", - (j == 0) ? "" : ", ", - addr_to_str(name, sizeof(name), - entry->addr, entry->port)); - } - printf("]\n"); - } - - return EXIT_SUCCESS; -} - -static int cluster_parser(int ch, char *opt) -{ - switch (ch) { - case 'c': - cluster_cmd_data.copies = atoi(opt); - break; - } - - return 0; -} - -static int cluster_shutdown(int argc, char **argv) -{ - return shutdown_sheepdog(); -} - -static struct subcommand cluster_cmd[] = { - {"info", NULL, "aprh", "show cluster information", - 0, cluster_info}, - {"format", NULL, "caph", "create a Sheepdog storage", - 0, cluster_format}, - {"shutdown", NULL, "aph", "stop Sheepdog", - SUBCMD_FLAG_NEED_NODELIST, cluster_shutdown}, - {NULL,}, -}; - -static struct { - const char *name; - struct subcommand *sub; - int (*parser)(int, char *); -} commands[] = { - {"vdi", vdi_cmd, - vdi_parser,}, - {"node", node_cmd,}, - {"cluster", cluster_cmd, - cluster_parser,}, -}; - static int (*command_parser)(int, char *); static int (*command_fn)(int, char **); static const char *command_options; @@ -1697,13 +171,13 @@ static struct option *build_long_options(const char *opts) return lopts; } -static unsigned long setup_command(char *cmd, char *subcmd) +static unsigned long setup_command(struct command *commands, char *cmd, char *subcmd) { int i, found = 0; struct subcommand *s; unsigned long flags = 0; - for (i = 0; i < ARRAY_SIZE(commands); i++) { + for (i = 0; commands[i].name; i++) { if (!strcmp(commands[i].name, cmd)) { found = 1; if (commands[i].parser) @@ -1714,7 +188,7 @@ static unsigned long setup_command(char *cmd, char *subcmd) if (!found) { fprintf(stderr, "'%s' is not a valid command\n", cmd); - usage(EXIT_USAGE); + usage(commands, EXIT_USAGE); } for (s = commands[i].sub; s->name; s++) { @@ -1739,7 +213,7 @@ static unsigned long setup_command(char *cmd, char *subcmd) return flags; } -static void usage(int status) +static void usage(struct command *commands, int status) { int i; struct subcommand *s; @@ -1752,7 +226,7 @@ static void usage(int status) printf("Sheepdog Administrator Utilty\n"); printf("\n"); printf("Command syntax:\n"); - for (i = 0; i < ARRAY_SIZE(commands); i++) { + for (i = 0; commands[i].name; i++) { for (s = commands[i].sub; s->name; s++) { sprintf(name, "%s %s", commands[i].name, s->name); printf(" %-24s%s\n", name, s->desc); @@ -1804,11 +278,18 @@ int main(int argc, char **argv) unsigned long flags; struct option *long_options; const char *short_options; + struct command commands[] = { + vdi_command, + node_command, + cluster_command, + {NULL,} + }; + if (argc < 3) - usage(0); + usage(commands, 0); - flags = setup_command(argv[1], argv[2]); + flags = setup_command(commands, argv[1], argv[2]); optind = 3; @@ -1832,13 +313,13 @@ int main(int argc, char **argv) subcommand_usage(argv[1], argv[2], EXIT_SUCCESS); break; case '?': - usage(EXIT_USAGE); + usage(commands, EXIT_USAGE); break; default: if (command_parser) command_parser(ch, optarg); else - usage(EXIT_USAGE); + usage(commands, EXIT_USAGE); break; } } diff --git a/collie/collie.h b/collie/collie.h new file mode 100644 index 0000000..98fba4c --- /dev/null +++ b/collie/collie.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef __COLLIE_H__ +#define __COLLIE_H__ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "sheepdog_proto.h" +#include "sheep.h" +#include "exits.h" + +#define SUBCMD_FLAG_NEED_NODELIST (1 << 0) +#define SUBCMD_FLAG_NEED_THIRD_ARG (1 << 1) + +#define TEXT_NORMAL "\033[0m" +#define TEXT_BOLD "\033[1m" + +struct sd_option { + int val; + const char *name; + int has_arg; + const char *desc; +}; + +struct command { + const char *name; + struct subcommand *sub; + int (*parser)(int, char *); +}; + +struct subcommand { + const char *name; + const char *arg; + const char *opts; + const char *desc; + unsigned long flags; + int (*fn)(int, char **); +}; + +extern const char *sdhost; +extern int sdport; +extern int highlight; +extern int raw_output; + +extern uint64_t node_list_version; +extern struct sheepdog_node_list_entry node_list_entries[SD_MAX_NODES]; +extern struct sheepdog_vnode_list_entry vnode_list_entries[SD_MAX_VNODES]; +extern int nr_nodes, nr_vnodes; +extern unsigned master_idx; + +int is_current(struct sheepdog_inode *i); +char *size_to_str(uint64_t _size, char *str, int str_size); +typedef void (*vdi_parser_func_t)(uint32_t vid, char *name, char *tag, + uint32_t snapid, uint32_t flags, + struct sheepdog_inode *i, void *data); +int parse_vdi(vdi_parser_func_t func, size_t size, void *data); +int sd_read_object(uint64_t oid, void *data, unsigned int datalen, + uint64_t offset); +int sd_write_object(uint64_t oid, void *data, unsigned int datalen, + uint64_t offset, uint32_t flags, int copies, int create); + +extern struct command vdi_command; +extern struct command node_command; +extern struct command cluster_command; + +#endif diff --git a/collie/common.c b/collie/common.c new file mode 100644 index 0000000..b58c192 --- /dev/null +++ b/collie/common.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "collie.h" + +int is_current(struct sheepdog_inode *i) +{ + return !i->snap_ctime; +} + +char *size_to_str(uint64_t _size, char *str, int str_size) +{ + const char *units[] = {"MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; + int i = 0; + double size; + + if (raw_output) { + snprintf(str, str_size, "%" PRIu64, _size); + return str; + } + + size = (double)_size; + size /= 1024 * 1024; + while (i < ARRAY_SIZE(units) && size >= 1024) { + i++; + size /= 1024; + } + + if (size >= 10) + snprintf(str, str_size, "%.0lf %s", size, units[i]); + else + snprintf(str, str_size, "%.1lf %s", size, units[i]); + + return str; +} + +int sd_read_object(uint64_t oid, void *data, unsigned int datalen, + uint64_t offset) +{ + struct sd_obj_req hdr; + struct sd_obj_rsp *rsp = (struct sd_obj_rsp *)&hdr; + char name[128]; + int n, fd, ret; + unsigned wlen = 0, rlen = datalen; + + n = obj_to_sheep(vnode_list_entries, nr_vnodes, oid, 0); + + addr_to_str(name, sizeof(name), vnode_list_entries[n].addr, 0); + + fd = connect_to(name, vnode_list_entries[n].port); + if (fd < 0) { + fprintf(stderr, "failed to connect %s:%d\n", name, + vnode_list_entries[n].port); + return SD_RES_EIO; + } + + memset(&hdr, 0, sizeof(hdr)); + hdr.epoch = node_list_version; + hdr.opcode = SD_OP_READ_OBJ; + hdr.oid = oid; + /* use direct to avoid checking consistency */ + hdr.flags = SD_FLAG_CMD_DIRECT; + hdr.data_length = rlen; + hdr.offset = offset; + + ret = exec_req(fd, (struct sd_req *)&hdr, data, &wlen, &rlen); + close(fd); + + if (ret) { + fprintf(stderr, "failed to read object, %lx\n", oid); + return SD_RES_EIO; + } + + if (rsp->result != SD_RES_SUCCESS) { + fprintf(stderr, "failed to read object, %lx %s\n", oid, + sd_strerror(rsp->result)); + return rsp->result; + } + + return SD_RES_SUCCESS; +} + +int sd_write_object(uint64_t oid, void *data, unsigned int datalen, + uint64_t offset, uint32_t flags, int copies, int create) +{ + struct sd_obj_req hdr; + struct sd_obj_rsp *rsp = (struct sd_obj_rsp *)&hdr; + int fd, ret; + unsigned wlen = datalen, rlen; + + fd = connect_to(sdhost, sdport); + if (fd < 0) { + fprintf(stderr, "failed to connect\n"); + return SD_RES_EIO; + } + + memset(&hdr, 0, sizeof(hdr)); + hdr.epoch = node_list_version; + if (create) + hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ; + else + hdr.opcode = SD_OP_WRITE_OBJ; + hdr.oid = oid; + hdr.copies = copies; + hdr.data_length = wlen; + hdr.flags = (flags & ~SD_FLAG_CMD_DIRECT) | SD_FLAG_CMD_WRITE; + hdr.offset = offset; + + ret = exec_req(fd, (struct sd_req *)&hdr, data, &wlen, &rlen); + close(fd); + + if (ret) { + fprintf(stderr, "failed to write object, %lx\n", oid); + return SD_RES_EIO; + } + if (rsp->result != SD_RES_SUCCESS) { + fprintf(stderr, "failed to write object, %lx %s\n", oid, + sd_strerror(rsp->result)); + return rsp->result; + } + + return SD_RES_SUCCESS; +} + +int parse_vdi(vdi_parser_func_t func, size_t size, void *data) +{ + int ret, fd; + unsigned long nr; + static struct sheepdog_inode i; + struct sd_req req; + static DECLARE_BITMAP(vdi_inuse, SD_NR_VDIS); + unsigned int rlen, wlen = 0; + + fd = connect_to(sdhost, sdport); + if (fd < 0) + return fd; + + memset(&req, 0, sizeof(req)); + + req.opcode = SD_OP_READ_VDIS; + req.data_length = sizeof(vdi_inuse); + req.epoch = node_list_version; + + rlen = sizeof(vdi_inuse); + ret = exec_req(fd, &req, vdi_inuse, &wlen, &rlen); + if (ret < 0) { + close(fd); + return ret; + } + close(fd); + + for (nr = 0; nr < SD_NR_VDIS; nr++) { + uint64_t oid; + + if (!test_bit(nr, vdi_inuse)) + continue; + + oid = vid_to_vdi_oid(nr); + + memset(&i, 0, sizeof(i)); + ret = sd_read_object(oid, &i, SD_INODE_HEADER_SIZE, 0); + if (ret != SD_RES_SUCCESS) { + fprintf(stderr, "failed to read a inode header\n"); + continue; + } + + if (i.name[0] == '\0') /* this vdi is deleted */ + continue; + + if (size > SD_INODE_HEADER_SIZE) { + rlen = DIV_ROUND_UP(i.vdi_size, SD_DATA_OBJ_SIZE) * + sizeof(i.data_vdi_id[0]); + if (rlen > size - SD_INODE_HEADER_SIZE) + rlen = size - SD_INODE_HEADER_SIZE; + + ret = sd_read_object(oid, ((char *)&i) + SD_INODE_HEADER_SIZE, + rlen, SD_INODE_HEADER_SIZE); + + if (ret != SD_RES_SUCCESS) { + fprintf(stderr, "failed to read inode\n"); + continue; + } + } + + func(i.vdi_id, i.name, i.tag, i.snap_id, 0, &i, data); + } + + return 0; +} diff --git a/collie/node.c b/collie/node.c new file mode 100644 index 0000000..a30a013 --- /dev/null +++ b/collie/node.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "collie.h" + +static void cal_total_vdi_size(uint32_t vid, char *name, char * tag, + uint32_t snapid, uint32_t flags, + struct sheepdog_inode *i, void *data) +{ + uint64_t *size = data; + + if (is_current(i)) + *size += i->vdi_size; +} + +static int node_list(int argc, char **argv) +{ + int i; + + if (!raw_output) { + printf(" Idx - Host:Port Vnodes Zone\n"); + printf("---------------------------------------------\n"); + } + for (i = 0; i < nr_nodes; i++) { + char data[128]; + + addr_to_str(data, sizeof(data), node_list_entries[i].addr, + node_list_entries[i].port); + + if (i == master_idx) { + if (highlight) + printf(TEXT_BOLD); + printf(raw_output ? "* %d %s %d %d\n" : "* %4d - %-20s\t%d%11d\n", + i, data, node_list_entries[i].nr_vnodes, + node_list_entries[i].zone); + if (highlight) + printf(TEXT_NORMAL); + } else + printf(raw_output ? "- %d %s %d %d\n" : " %4d - %-20s\t%d%11d\n", + i, data, node_list_entries[i].nr_vnodes, + node_list_entries[i].zone); + } + + return EXIT_SUCCESS; +} + +static int node_info(int argc, char **argv) +{ + int i, ret, success = 0; + uint64_t total_size = 0, total_avail = 0, total_vdi_size = 0; + char total_str[8], avail_str[8], vdi_size_str[8]; + + if (!raw_output) + printf("Id\tSize\tUsed\tUse%%\n"); + + for (i = 0; i < nr_nodes; i++) { + char name[128]; + int fd; + unsigned wlen, rlen; + struct sd_node_req req; + struct sd_node_rsp *rsp = (struct sd_node_rsp *)&req; + char store_str[8], free_str[8]; + + addr_to_str(name, sizeof(name), node_list_entries[i].addr, 0); + + fd = connect_to(name, node_list_entries[i].port); + if (fd < 0) + return 1; + + memset(&req, 0, sizeof(req)); + + req.opcode = SD_OP_STAT_SHEEP; + req.epoch = node_list_version; + + wlen = 0; + rlen = 0; + ret = exec_req(fd, (struct sd_req *)&req, NULL, &wlen, &rlen); + close(fd); + + size_to_str(rsp->store_size, store_str, sizeof(store_str)); + size_to_str(rsp->store_size - rsp->store_free, free_str, + sizeof(free_str)); + if (!ret && rsp->result == SD_RES_SUCCESS) { + printf(raw_output ? "%d %s %s %d%%\n" : "%2d\t%s\t%s\t%3d%%\n", + i, store_str, free_str, + (int)(((double)(rsp->store_size - rsp->store_free) / rsp->store_size) * 100)); + success++; + } + + total_size += rsp->store_size; + total_avail += rsp->store_free; + } + + if (success == 0) { + fprintf(stderr, "cannot get information from any nodes\n"); + return EXIT_SYSFAIL; + } + + parse_vdi(cal_total_vdi_size, SD_INODE_HEADER_SIZE, &total_vdi_size); + + size_to_str(total_size, total_str, sizeof(total_str)); + size_to_str(total_size - total_avail, avail_str, sizeof(avail_str)); + size_to_str(total_vdi_size, vdi_size_str, sizeof(vdi_size_str)); + printf(raw_output ? "Total %s %s %d%% %s\n" + : "\nTotal\t%s\t%s\t%3d%%, total virtual VDI Size\t%s\n", + total_str, avail_str, + (int)(((double)(total_size - total_avail) / total_size) * 100), + vdi_size_str); + + return EXIT_SUCCESS; +} + +static struct subcommand node_cmd[] = { + {"list", NULL, "aprh", "list nodes", + SUBCMD_FLAG_NEED_NODELIST, node_list}, + {"info", NULL, "aprh", "show each node information", + SUBCMD_FLAG_NEED_NODELIST, node_info}, + {NULL,}, +}; + +struct command node_command = { + "node", + node_cmd, +}; diff --git a/collie/vdi.c b/collie/vdi.c new file mode 100644 index 0000000..5ab6c25 --- /dev/null +++ b/collie/vdi.c @@ -0,0 +1,1025 @@ +/* + * Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <ctype.h> +#include <time.h> +#include <sys/time.h> + +#include "collie.h" +#include "treeview.h" + +struct vdi_cmd_data { + unsigned int index; + int snapshot_id; + char snapshot_tag[SD_MAX_VDI_TAG_LEN]; + int exclusive; + int delete; + int prealloc; +} vdi_cmd_data = { ~0, }; + +struct get_vdi_info { + char *name; + char *tag; + uint32_t vid; + uint32_t snapid; +}; + +static int parse_option_size(const char *value, uint64_t *ret) +{ + char *postfix; + double sizef; + + sizef = strtod(value, &postfix); + switch (*postfix) { + case 'T': + sizef *= 1024; + case 'G': + sizef *= 1024; + case 'M': + sizef *= 1024; + case 'K': + case 'k': + sizef *= 1024; + case 'b': + case '\0': + *ret = (uint64_t) sizef; + break; + default: + fprintf(stderr, "invalid parameter, %s\n", value); + fprintf(stderr, "You may use k, M, G or T suffixes for " + "kilobytes, megabytes, gigabytes and terabytes.\n"); + return -1; + } + + return 0; +} + +static void print_vdi_list(uint32_t vid, char *name, char *tag, uint32_t snapid, + uint32_t flags, struct sheepdog_inode *i, void *data) +{ + int idx; + uint64_t my_objs, cow_objs; + char vdi_size_str[16], my_objs_str[16], cow_objs_str[16]; + time_t ti; + struct tm tm; + char dbuf[128]; + struct get_vdi_info *info = data; + + if (info && strcmp(name, info->name) != 0) + return; + + ti = i->ctime >> 32; + if (raw_output) { + snprintf(dbuf, sizeof(dbuf), "%" PRIu64, (uint64_t) ti); + } else { + localtime_r(&ti, &tm); + strftime(dbuf, sizeof(dbuf), + "%Y-%m-%d %H:%M", &tm); + } + + my_objs = 0; + cow_objs = 0; + for (idx = 0; idx < MAX_DATA_OBJS; idx++) { + if (!i->data_vdi_id[idx]) + continue; + if (is_data_obj_writeable(i, idx)) + my_objs++; + else + cow_objs++; + } + + size_to_str(i->vdi_size, vdi_size_str, sizeof(vdi_size_str)); + size_to_str(my_objs * SD_DATA_OBJ_SIZE, my_objs_str, sizeof(my_objs_str)); + size_to_str(cow_objs * SD_DATA_OBJ_SIZE, cow_objs_str, sizeof(cow_objs_str)); + + if (raw_output) { + printf("%c ", is_current(i) ? '=' : 's'); + while (*name) { + if (isspace(*name) || *name == '\\') + putchar('\\'); + putchar(*name++); + } + printf(" %d %s %s %s %s %" PRIx32 "\n", snapid, + vdi_size_str, my_objs_str, cow_objs_str, dbuf, vid); + } else { + printf("%c %-8s %5d %7s %7s %7s %s %7" PRIx32 "\n", + is_current(i) ? ' ' : 's', name, snapid, + vdi_size_str, my_objs_str, cow_objs_str, dbuf, vid); + } +} + +static void print_vdi_tree(uint32_t vid, char *name, char * tag, uint32_t snapid, + uint32_t flags, struct sheepdog_inode *i, void *data) +{ + time_t ti; + struct tm tm; + char buf[128]; + + if (is_current(i)) + strcpy(buf, "(You Are Here)"); + else { + ti = i->ctime >> 32; + localtime_r(&ti, &tm); + + strftime(buf, sizeof(buf), + "[%Y-%m-%d %H:%M]", &tm); + } + + add_vdi_tree(name, buf, vid, i->parent_vdi_id, highlight && is_current(i)); +} + +static void print_vdi_graph(uint32_t vid, char *name, char * tag, uint32_t snapid, + uint32_t flags, struct sheepdog_inode *i, void *data) +{ + time_t ti; + struct tm tm; + char dbuf[128], tbuf[128], size_str[128]; + + ti = i->ctime >> 32; + localtime_r(&ti, &tm); + + strftime(dbuf, sizeof(dbuf), "%Y-%m-%d", &tm); + strftime(tbuf, sizeof(tbuf), "%H:%M:%S", &tm); + size_to_str(i->vdi_size, size_str, sizeof(size_str)); + + printf(" \"%x\" -> \"%x\";\n", i->parent_vdi_id, vid); + printf(" \"%x\" [\n" + " group = \"%s\",\n" + " label = \"", + vid, name); + printf("name: %10s\\n" + "tag : %10x\\n" + "size: %10s\\n" + "date: %10s\\n" + "time: %10s", + name, snapid, size_str, dbuf, tbuf); + + if (is_current(i)) + printf("\",\n color=\"red\"\n ];\n\n"); + else + printf("\"\n ];\n\n"); + +} + +static void get_oid(uint32_t vid, char *name, char *tag, uint32_t snapid, + uint32_t flags, struct sheepdog_inode *i, void *data) +{ + struct get_vdi_info *info = data; + + if (info->name) { + if (info->tag) { + if (!strcmp(name, info->name) && !strcmp(tag, info->tag)) + info->vid = vid; + } else if (info->snapid) { + if (!strcmp(name, info->name) && snapid == info->snapid) + info->vid = vid; + } else { + if (!strcmp(name, info->name)) + info->vid = vid; + } + } +} + +typedef void (*obj_parser_func_t)(char *sheep, uint64_t oid, + struct sd_obj_rsp *rsp, char *buf, void *data); + +static void do_print_obj(char *sheep, uint64_t oid, struct sd_obj_rsp *rsp, + char *buf, void *data) +{ + switch (rsp->result) { + case SD_RES_SUCCESS: + printf("%s: has the object (should be %d copies)\n", + sheep, rsp->copies); + break; + case SD_RES_NO_OBJ: + printf("%s: doesn't have\n", sheep); + break; + case SD_RES_OLD_NODE_VER: + case SD_RES_NEW_NODE_VER: + printf("the node list has changed, try again\n"); + break; + default: + printf("%s: hit an expected error, %d\n", + sheep, rsp->result); + break; + } +} + +struct get_data_oid_info { + int success; + uint64_t data_oid; + unsigned idx; +}; + +static void get_data_oid(char *sheep, uint64_t oid, struct sd_obj_rsp *rsp, + char *buf, void *data) +{ + struct get_data_oid_info *info = data; + struct sheepdog_inode *inode = (struct sheepdog_inode *)buf; + + switch (rsp->result) { + case SD_RES_SUCCESS: + if (info->success) + break; + info->success = 1; + if (inode->data_vdi_id[info->idx]) + info->data_oid = vid_to_data_oid(inode->data_vdi_id[info->idx], info->idx); + break; + case SD_RES_NO_OBJ: + break; + case SD_RES_OLD_NODE_VER: + case SD_RES_NEW_NODE_VER: + printf("the node list has changed, try again\n"); + break; + default: + printf("%s: hit an expected error, %d\n", + sheep, rsp->result); + break; + } +} + +static void parse_objs(uint64_t oid, obj_parser_func_t func, void *data) +{ + char name[128]; + int i, fd, ret; + char *buf; + + buf = zalloc(sizeof(struct sheepdog_inode)); + if (!buf) { + fprintf(stderr, "out of memory\n"); + return; + } + + for (i = 0; i < nr_nodes; i++) { + unsigned wlen = 0, rlen = sizeof(struct sheepdog_inode); + struct sd_obj_req hdr; + struct sd_obj_rsp *rsp = (struct sd_obj_rsp *)&hdr; + + addr_to_str(name, sizeof(name), node_list_entries[i].addr, 0); + + fd = connect_to(name, node_list_entries[i].port); + if (fd < 0) + break; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.opcode = SD_OP_READ_OBJ; + hdr.data_length = rlen; + hdr.flags = SD_FLAG_CMD_DIRECT; + hdr.oid = oid; + hdr.epoch = node_list_version; + + ret = exec_req(fd, (struct sd_req *)&hdr, buf, &wlen, &rlen); + close(fd); + + sprintf(name + strlen(name), ":%d", node_list_entries[i].port); + + if (ret) + printf("%s: can't connect\n", name); + else + func(name, oid, rsp, buf, data); + } + + free(buf); +} + + +static int vdi_list(int argc, char **argv) +{ + char *vdiname = argv[optind]; + + if (!raw_output) { + printf(" name id size used shared creation time vdi id\n"); + printf("------------------------------------------------------------------\n"); + } + + if (vdiname) { + struct get_vdi_info info; + memset(&info, 0, sizeof(info)); + info.name = vdiname; + parse_vdi(print_vdi_list, SD_INODE_SIZE, &info); + return EXIT_SUCCESS; + } else { + parse_vdi(print_vdi_list, SD_INODE_SIZE, NULL); + return EXIT_SUCCESS; + } +} + +static int vdi_tree(int argc, char **argv) +{ + init_tree(); + parse_vdi(print_vdi_tree, SD_INODE_HEADER_SIZE, NULL); + dump_tree(); + + return EXIT_SUCCESS; +} + +static int vdi_graph(int argc, char **argv) +{ + /* print a header */ + printf("digraph G {\n"); + printf(" node [shape = \"box\", fontname = \"Courier\"];\n\n"); + printf(" \"0\" [shape = \"ellipse\", label = \"root\"];\n\n"); + + parse_vdi(print_vdi_graph, SD_INODE_HEADER_SIZE, NULL); + + /* print a footer */ + printf("}\n"); + + return EXIT_SUCCESS; +} + +static int find_vdi_name(char *vdiname, uint32_t snapid, const char *tag, + uint32_t *vid, int for_snapshot) +{ + int ret, fd; + struct sd_vdi_req hdr; + struct sd_vdi_rsp *rsp = (struct sd_vdi_rsp *)&hdr; + unsigned int wlen, rlen = 0; + char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN]; + + fd = connect_to(sdhost, sdport); + if (fd < 0) + return -1; + + memset(buf, 0, sizeof(buf)); + strncpy(buf, vdiname, SD_MAX_VDI_LEN); + strncpy(buf + SD_MAX_VDI_LEN, tag, SD_MAX_VDI_TAG_LEN); + + memset(&hdr, 0, sizeof(hdr)); + if (for_snapshot) + hdr.opcode = SD_OP_GET_VDI_INFO; + else + hdr.opcode = SD_OP_LOCK_VDI; + wlen = SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN; + hdr.proto_ver = SD_PROTO_VER; + hdr.data_length = wlen; + hdr.snapid = snapid; + hdr.flags = SD_FLAG_CMD_WRITE; + + ret = exec_req(fd, (struct sd_req *)&hdr, buf, &wlen, &rlen); + if (ret) { + ret = -1; + goto out; + } + + if (rsp->result != SD_RES_SUCCESS) { + fprintf(stderr, "cannot get vdi info, %s, %s %d %s\n", + sd_strerror(rsp->result), vdiname, snapid, tag); + ret = -1; + goto out; + } + *vid = rsp->vdi_id; + + ret = 0; +out: + close(fd); + return ret; +} + +static int do_vdi_create(char *vdiname, int64_t vdi_size, uint32_t base_vid, + uint32_t *vdi_id, int snapshot) +{ + struct sd_vdi_req hdr; + struct sd_vdi_rsp *rsp = (struct sd_vdi_rsp *)&hdr; + int fd, ret; + unsigned int wlen, rlen = 0; + char buf[SD_MAX_VDI_LEN]; + + fd = connect_to(sdhost, sdport); + if (fd < 0) { + fprintf(stderr, "failed to connect\n"); + return EXIT_SYSFAIL; + } + + memset(buf, 0, sizeof(buf)); + strncpy(buf, vdiname, SD_MAX_VDI_LEN); + + memset(&hdr, 0, sizeof(hdr)); + hdr.opcode = SD_OP_NEW_VDI; + hdr.base_vdi_id = base_vid; + + wlen = SD_MAX_VDI_LEN; + + hdr.flags = SD_FLAG_CMD_WRITE; + hdr.snapid = snapshot; + + hdr.data_length = wlen; + hdr.vdi_size = vdi_size; + + ret = exec_req(fd, (struct sd_req *)&hdr, buf, &wlen, &rlen); + + close(fd); + + if (ret) { + fprintf(stderr, "failed to send a request\n"); + return EXIT_SYSFAIL; + } + + if (rsp->result != SD_RES_SUCCESS) { + fprintf(stderr, "%s, %s\n", sd_strerror(rsp->result), vdiname); + return EXIT_FAILURE; + } + + if (vdi_id) + *vdi_id = rsp->vdi_id; + + return EXIT_SUCCESS; +} + +static int vdi_create(int argc, char **argv) +{ + char *vdiname = argv[optind++]; + uint64_t size; + uint32_t vid; + uint64_t oid; + int idx, max_idx, ret; + struct sheepdog_inode *inode = NULL; + char *buf = NULL; + + if (!argv[optind]) { + fprintf(stderr, "please specify the size of vdi\n"); + return EXIT_USAGE; + } + ret = parse_option_size(argv[optind], &size); + if (ret < 0) + return EXIT_USAGE; + if (size > SD_MAX_VDI_SIZE) { + fprintf(stderr, "too big image size, %s\n", argv[optind]); + return EXIT_USAGE; + } + + ret = do_vdi_create(vdiname, size, 0, &vid, 0); + if (ret != EXIT_SUCCESS || !vdi_cmd_data.prealloc) + goto out; + + inode = malloc(sizeof(*inode)); + buf = zalloc(SD_DATA_OBJ_SIZE); + if (!inode || !buf) { + fprintf(stderr, "oom\n"); + ret = EXIT_SYSFAIL; + goto out; + } + + ret = sd_read_object(vid_to_vdi_oid(vid), inode, sizeof(*inode), 0); + if (ret != SD_RES_SUCCESS) { + fprintf(stderr, "failed to read a newly created vdi object\n"); + ret = EXIT_FAILURE; + goto out; + } + max_idx = DIV_ROUND_UP(size, SD_DATA_OBJ_SIZE); + + for (idx = 0; idx < max_idx; idx++) { + oid = vid_to_data_oid(vid, idx); + + ret = sd_write_object(oid, buf, SD_DATA_OBJ_SIZE, 0, 0, + inode->nr_copies, 1); + if (ret != SD_RES_SUCCESS) { + ret = EXIT_FAILURE; + goto out; + } + + inode->data_vdi_id[idx] = vid; + ret = sd_write_object(vid_to_vdi_oid(vid), &vid, sizeof(vid), + SD_INODE_HEADER_SIZE + sizeof(vid) * idx, 0, + inode->nr_copies, 0); + if (ret) { + ret = EXIT_FAILURE; + goto out; + } + } + ret = EXIT_SUCCESS; +out: + free(inode); + free(buf); + return ret; +} + +static int vdi_snapshot(int argc, char **argv) +{ + char *vdiname = argv[optind++]; + uint32_t vid; + int ret; + char buf[SD_INODE_HEADER_SIZE]; + struct sheepdog_inode *inode = (struct sheepdog_inode *)buf; + + if (vdi_cmd_data.snapshot_id != 0) { + fprintf(stderr, "please specify a non-integer value for " + "a snapshot tag name\n"); + return EXIT_USAGE; + } + + ret = find_vdi_name(vdiname, 0, "", &vid, 0); + if (ret < 0) { + fprintf(stderr, "failed to open vdi %s\n", vdiname); + return EXIT_FAILURE; + } + + ret = sd_read_object(vid_to_vdi_oid(vid), inode, SD_INODE_HEADER_SIZE, 0); + if (ret != SD_RES_SUCCESS) { + fprintf(stderr, "failed to read an inode header\n"); + return EXIT_FAILURE; + } + + if (vdi_cmd_data.snapshot_tag[0]) { + ret = sd_write_object(vid_to_vdi_oid(vid), vdi_cmd_data.snapshot_tag, + SD_MAX_VDI_TAG_LEN, + offsetof(struct sheepdog_inode, tag), + 0, inode->nr_copies, 0); + } + + return do_vdi_create(vdiname, inode->vdi_size, vid, NULL, 1); +} + +static int vdi_clone(int argc, char **argv) +{ + char *src_vdi = argv[optind++], *dst_vdi; + uint32_t base_vid, new_vid; + uint64_t oid; + int idx, max_idx, ret; + struct sheepdog_inode *inode = NULL; + char *buf = NULL; + + dst_vdi = argv[optind]; + if (!dst_vdi) { + fprintf(stderr, "a dst vdi must be specified\n"); + ret = EXIT_USAGE; + goto out; + } + + if (!vdi_cmd_data.snapshot_id && !vdi_cmd_data.snapshot_tag[0]) { + fprintf(stderr, "it is not supported to create a clone image of " + "the non-snapshot vdi\n"); + fprintf(stderr, "please specify a '-s' option\n"); + ret = EXIT_USAGE; + goto out; + } + + ret = find_vdi_name(src_vdi, vdi_cmd_data.snapshot_id, + vdi_cmd_data.snapshot_tag, &base_vid, 0); + if (ret < 0) { + fprintf(stderr, "failed to open vdi %s\n", src_vdi); + ret = EXIT_FAILURE; + goto out; + } + + inode = malloc(sizeof(*inode)); + if (!inode) { + fprintf(stderr, "oom\n"); + ret = EXIT_SYSFAIL; + goto out; + } + ret = sd_read_object(vid_to_vdi_oid(base_vid), inode, SD_INODE_SIZE, 0); + if (ret != SD_RES_SUCCESS) { + fprintf(stderr, "failed to read a base inode\n"); + ret = EXIT_FAILURE; + goto out; + } + + ret = do_vdi_create(dst_vdi, inode->vdi_size, base_vid, &new_vid, 0); + if (ret != EXIT_SUCCESS || !vdi_cmd_data.prealloc) + goto out; + + buf = zalloc(SD_DATA_OBJ_SIZE); + if (!buf) { + fprintf(stderr, "oom\n"); + ret = EXIT_SYSFAIL; + goto out; + } + + max_idx = DIV_ROUND_UP(inode->vdi_size, SD_DATA_OBJ_SIZE); + + for (idx = 0; idx < max_idx; idx++) { + if (inode->data_vdi_id[idx]) { + oid = vid_to_data_oid(inode->data_vdi_id[idx], idx); + ret = sd_read_object(oid, buf, SD_DATA_OBJ_SIZE, 0); + if (ret) { + ret = EXIT_FAILURE; + goto out; + } + } else + memset(buf, 0, SD_DATA_OBJ_SIZE); + + oid = vid_to_data_oid(new_vid, idx); + ret = sd_write_object(oid, buf, SD_DATA_OBJ_SIZE, 0, 0, + inode->nr_copies, 1); + if (ret != SD_RES_SUCCESS) { + ret = EXIT_FAILURE; + goto out; + } + + ret = sd_write_object(vid_to_vdi_oid(new_vid), &new_vid, sizeof(new_vid), + SD_INODE_HEADER_SIZE + sizeof(new_vid) * idx, 0, + inode->nr_copies, 0); + if (ret) { + ret = EXIT_FAILURE; + goto out; + } + } + ret = EXIT_SUCCESS; +out: + free(inode); + free(buf); + return ret; +} + +static int vdi_resize(int argc, char **argv) +{ + char *vdiname = argv[optind++]; + uint64_t new_size; + uint32_t vid; + int ret; + char buf[SD_INODE_HEADER_SIZE]; + struct sheepdog_inode *inode = (struct sheepdog_inode *)buf; + + if (!argv[optind]) { + fprintf(stderr, "please specify a new size of vdi\n"); + return EXIT_USAGE; + } + ret = parse_option_size(argv[optind], &new_size); + if (ret < 0) + return EXIT_USAGE; + if (new_size > SD_MAX_VDI_SIZE) { + fprintf(stderr, "too big image size, %s\n", argv[optind]); + return EXIT_USAGE; + } + + ret = find_vdi_name(vdiname, 0, "", &vid, 0); + if (ret < 0) { + fprintf(stderr, "failed to open vdi %s\n", vdiname); + return EXIT_FAILURE; + } + + ret = sd_read_object(vid_to_vdi_oid(vid), inode, SD_INODE_HEADER_SIZE, 0); + if (ret != SD_RES_SUCCESS) { + fprintf(stderr, "failed to read an inode header\n"); + return EXIT_FAILURE; + } + + if (new_size < inode->vdi_size) { + fprintf(stderr, "shrinking is not implemented\n"); + return EXIT_USAGE; + } + inode->vdi_size = new_size; + + ret = sd_write_object(vid_to_vdi_oid(vid), inode, SD_INODE_HEADER_SIZE, 0, + 0, inode->nr_copies, 0); + if (ret != SD_RES_SUCCESS) { + fprintf(stderr, "failed to update an inode header\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static int vdi_delete(int argc, char **argv) +{ + char *data = argv[optind]; + int fd, ret; + struct sd_vdi_req hdr; + struct sd_vdi_rsp *rsp = (struct sd_vdi_rsp *)&hdr; + unsigned rlen, wlen; + char vdiname[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN]; + + fd = connect_to(sdhost, sdport); + if (fd < 0) + return EXIT_SYSFAIL; + + memset(&hdr, 0, sizeof(hdr)); + + rlen = 0; + wlen = sizeof(vdiname); + + hdr.opcode = SD_OP_DEL_VDI; + hdr.snapid = vdi_cmd_data.snapshot_id; + hdr.epoch = node_list_version; + hdr.flags = SD_FLAG_CMD_WRITE; + hdr.data_length = wlen; + memset(vdiname, 0, sizeof(vdiname)); + strncpy(vdiname, data, SD_MAX_VDI_LEN); + strncpy(vdiname + SD_MAX_VDI_LEN, vdi_cmd_data.snapshot_tag, + SD_MAX_VDI_TAG_LEN); + + ret = exec_req(fd, (struct sd_req *)&hdr, vdiname, &wlen, &rlen); + close(fd); + + if (ret) { + fprintf(stderr, "failed to connect\n"); + return EXIT_SYSFAIL; + } + + if (rsp->result != SD_RES_SUCCESS) { + fprintf(stderr, "%s: %s\n", vdiname, sd_strerror(rsp->result)); + if (rsp->result == SD_RES_NO_VDI) + return EXIT_MISSING; + else + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static int vdi_object(int argc, char **argv) +{ + char *vdiname = argv[optind]; + unsigned idx = vdi_cmd_data.index; + int ret; + struct get_vdi_info info; + uint32_t vid; + + memset(&info, 0, sizeof(info)); + info.name = vdiname; + info.tag = vdi_cmd_data.snapshot_tag; + info.vid = 0; + info.snapid = vdi_cmd_data.snapshot_id; + + ret = parse_vdi(get_oid, SD_INODE_HEADER_SIZE, &info); + + vid = info.vid; + if (vid == 0) { + printf("No such vdi\n"); + return EXIT_MISSING; + } + + if (idx == ~0) { + printf("Looking for the inode object 0x%" PRIx32 " with %d nodes\n\n", + vid, nr_nodes); + parse_objs(vid_to_vdi_oid(vid), do_print_obj, NULL); + } else { + struct get_data_oid_info old_info; + + old_info.success = 0; + old_info.idx = idx; + + if (idx >= MAX_DATA_OBJS) { + printf("The offset is too large!\n"); + exit(EXIT_FAILURE); + } + + parse_objs(vid_to_vdi_oid(vid), get_data_oid, &old_info); + + if (old_info.success) { + if (old_info.data_oid) { + printf("Looking for the object 0x%" PRIx64 + " (the inode vid 0x%" PRIx32 " idx %u) with %d nodes\n\n", + old_info.data_oid, vid, idx, nr_nodes); + + parse_objs(old_info.data_oid, do_print_obj, NULL); + } else + printf("The inode object 0x%" PRIx32 " idx %u is not allocated\n", + vid, idx); + } else + printf("failed to read the inode object 0x%" PRIx32 "\n", vid); + } + + return EXIT_SUCCESS; +} + +static int find_vdi_attr_oid(char *vdiname, char *tag, uint32_t snapid, + char *key, uint32_t *vid, uint64_t *oid, + unsigned int *nr_copies, int creat, int excl) +{ + struct sd_vdi_req hdr; + struct sd_vdi_rsp *rsp = (struct sd_vdi_rsp *)&hdr; + int fd, ret; + unsigned int wlen, rlen; + char buf[SD_ATTR_HEADER_SIZE]; + + memset(buf, 0, sizeof(buf)); + strncpy(buf, vdiname, SD_MAX_VDI_LEN); + strncpy(buf + SD_MAX_VDI_LEN, vdi_cmd_data.snapshot_tag, + SD_MAX_VDI_TAG_LEN); + memcpy(buf + SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN, + &vdi_cmd_data.snapshot_id, sizeof(uint32_t)); + strncpy(buf + SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN + sizeof(uint32_t), + key, SD_MAX_VDI_ATTR_KEY_LEN); + + fd = connect_to(sdhost, sdport); + if (fd < 0) { + fprintf(stderr, "failed to connect\n\n"); + return SD_RES_EIO; + } + + memset(&hdr, 0, sizeof(hdr)); + hdr.opcode = SD_OP_GET_VDI_ATTR; + wlen = SD_ATTR_HEADER_SIZE; + rlen = 0; + hdr.proto_ver = SD_PROTO_VER; + hdr.data_length = wlen; + hdr.snapid = vdi_cmd_data.snapshot_id; + hdr.flags = SD_FLAG_CMD_WRITE; + if (creat) + hdr.flags |= SD_FLAG_CMD_CREAT; + if (excl) + hdr.flags |= SD_FLAG_CMD_EXCL; + + ret = exec_req(fd, (struct sd_req *)&hdr, buf, &wlen, &rlen); + if (ret) { + ret = SD_RES_EIO; + goto out; + } + + if (rsp->result != SD_RES_SUCCESS) { + ret = rsp->result; + goto out; + } + + *vid = rsp->vdi_id; + *oid = vid_to_attr_oid(rsp->vdi_id, rsp->attr_id); + *nr_copies = rsp->copies; + + ret = SD_RES_SUCCESS; +out: + close(fd); + return ret; +} + +static int vdi_setattr(int argc, char **argv) +{ + int ret; + uint64_t oid, attr_oid = 0; + uint32_t vid = 0, nr_copies = 0; + char *vdiname = argv[optind++], *key, *value; + uint64_t offset; + + key = argv[optind++]; + if (!key) { + fprintf(stderr, "please specify the name of key\n"); + return EXIT_USAGE; + } + + value = argv[optind++]; + if (!value && !vdi_cmd_data.delete) { + value = malloc(SD_MAX_VDI_ATTR_VALUE_LEN); + if (!value) { + fprintf(stderr, "failed to allocate memory\n"); + return EXIT_SYSFAIL; + } + + offset = 0; +reread: + ret = read(STDIN_FILENO, value + offset, + SD_MAX_VDI_ATTR_VALUE_LEN - offset); + if (ret < 0) { + fprintf(stderr, "failed to read from stdin, %m\n"); + return EXIT_SYSFAIL; + } + if (ret > 0) { + offset += ret; + goto reread; + } + } + + ret = find_vdi_attr_oid(vdiname, vdi_cmd_data.snapshot_tag, + vdi_cmd_data.snapshot_id, key, &vid, &attr_oid, + &nr_copies, !vdi_cmd_data.delete, + vdi_cmd_data.exclusive); + if (ret) { + if (ret == SD_RES_VDI_EXIST) { + fprintf(stderr, "the attribute already exists, %s\n", key); + return EXIT_EXISTS; + } else if (ret == SD_RES_NO_OBJ) { + fprintf(stderr, "no such attribute, %s\n", key); + return EXIT_MISSING; + } else if (ret == SD_RES_NO_VDI) { + fprintf(stderr, "vdi not found\n"); + return EXIT_MISSING; + } else + fprintf(stderr, "failed to find attr oid, %s\n", + sd_strerror(ret)); + return EXIT_FAILURE; + } + + oid = attr_oid; + + if (vdi_cmd_data.delete) + ret = sd_write_object(oid, (char *)"", 1, + offsetof(struct sheepdog_inode, name), 0, + nr_copies, 0); + else + ret = sd_write_object(oid, value, strlen(value), + SD_ATTR_HEADER_SIZE, SD_FLAG_CMD_TRUNCATE, + nr_copies, 0); + + if (ret != SD_RES_SUCCESS) { + fprintf(stderr, "failed to set attribute\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static int vdi_getattr(int argc, char **argv) +{ + struct sd_obj_req hdr; + struct sd_obj_rsp *rsp = (struct sd_obj_rsp *)&hdr; + int ret; + uint64_t oid, attr_oid = 0; + uint32_t vid = 0, nr_copies = 0; + char *vdiname = argv[optind++], *key, *value; + + key = argv[optind++]; + if (!key) { + fprintf(stderr, "please specify the name of key\n"); + return EXIT_USAGE; + } + + ret = find_vdi_attr_oid(vdiname, vdi_cmd_data.snapshot_tag, + vdi_cmd_data.snapshot_id, key, &vid, &attr_oid, + &nr_copies, 0, 0); + if (ret == SD_RES_NO_OBJ) { + fprintf(stderr, "no such attribute, %s\n", key); + return EXIT_MISSING; + } else if (ret == SD_RES_NO_VDI) { + fprintf(stderr, "vdi not found\n"); + return EXIT_MISSING; + } else if (ret) { + fprintf(stderr, "failed to find attr oid, %s\n", + sd_strerror(ret)); + return EXIT_MISSING; + } + + oid = attr_oid; + value = malloc(SD_MAX_VDI_ATTR_VALUE_LEN); + if (!value) { + fprintf(stderr, "failed to allocate memory\n"); + return EXIT_SYSFAIL; + } + + ret = sd_read_object(oid, value, SD_MAX_VDI_ATTR_VALUE_LEN, + SD_ATTR_HEADER_SIZE); + if (rsp->result == SD_RES_SUCCESS) { + printf("%s", value); + free(value); + return EXIT_SUCCESS; + } + + free(value); + return EXIT_FAILURE; +} + +static struct subcommand vdi_cmd[] = { + {"create", "<vdiname> <size>", "Paph", "create a image", + SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_create}, + {"snapshot", "<vdiname>", "saph", "create a snapshot", + SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_snapshot}, + {"clone", "<src vdi> <dst vdi>", "sPaph", "create a clone image", + SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_clone}, + {"delete", "<vdiname>", "saph", "delete a image", + SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_delete}, + {"list", "[vdiname]", "aprh", "list images", + SUBCMD_FLAG_NEED_NODELIST, vdi_list}, + {"tree", NULL, "aph", "show images in tree view format", + SUBCMD_FLAG_NEED_NODELIST, vdi_tree}, + {"graph", NULL, "aph", "show images with Graphviz dot format", + SUBCMD_FLAG_NEED_NODELIST, vdi_graph}, + {"object", "<vdiname>", "isaph", "show object information in the image", + SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_object}, + {"setattr", "<vdiname> <key> [value]", "dxaph", "set a vdi attribute", + SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_setattr}, + {"getattr", "<vdiname> <key>", "aph", "get a vdi attribute", + SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_getattr}, + {"resize", "<vdiname> <new size>", "aph", "resize a image", + SUBCMD_FLAG_NEED_NODELIST|SUBCMD_FLAG_NEED_THIRD_ARG, vdi_resize}, + {NULL,}, +}; + +static int vdi_parser(int ch, char *opt) +{ + switch (ch) { + case 'P': + vdi_cmd_data.prealloc = 1; + break; + case 'i': + vdi_cmd_data.index = atoi(opt); + break; + case 's': + vdi_cmd_data.snapshot_id = atoi(opt); + if (vdi_cmd_data.snapshot_id == 0) + strncpy(vdi_cmd_data.snapshot_tag, opt, + sizeof(vdi_cmd_data.snapshot_tag)); + break; + case 'x': + vdi_cmd_data.exclusive = 1; + break; + case 'd': + vdi_cmd_data.delete = 1; + break; + } + + return 0; +} + +struct command vdi_command = { + "vdi", + vdi_cmd, + vdi_parser +}; -- 1.7.2.5 |