diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8dc2ff91..635e6259 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -153,7 +153,8 @@ target_link_libraries(vitastor-nbd # vitastor-cli add_executable(vitastor-cli - cli.cpp cli_alloc_osd.cpp cli_ls.cpp cli_create.cpp cli_modify.cpp cli_flatten.cpp cli_merge.cpp cli_rm.cpp cli_snap_rm.cpp + cli.cpp cli_alloc_osd.cpp cli_simple_offsets.cpp + cli_ls.cpp cli_create.cpp cli_modify.cpp cli_flatten.cpp cli_merge.cpp cli_rm.cpp cli_snap_rm.cpp ) target_link_libraries(vitastor-cli vitastor_client diff --git a/src/cli.cpp b/src/cli.cpp index 8bc80cd5..934cc826 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -132,6 +132,15 @@ void cli_tool_t::help() "\n" "%s alloc-osd\n" " Allocate a new OSD number and reserve it by creating empty /osd/stats/ key.\n" + "%s simple-offsets \n" + " Calculate offsets for simple&stupid (no superblock) OSD deployment. Options:\n" + " --object_size 128k Set blockstore block size\n" + " --bitmap_granularity 4k Set bitmap granularity\n" + " --journal_size 16M Set journal size\n" + " --device_block_size 4k Set device block size\n" + " --journal_offset 0 Set journal offset\n" + " --device_size 0 Set device size\n" + " --format text Result format: json, options, env, or text\n" "\n" "GLOBAL OPTIONS:\n" " --etcd_address \n" @@ -142,7 +151,7 @@ void cli_tool_t::help() " --no-color Disable colored output\n" " --json JSON output\n" , - exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name + exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name ); exit(0); } @@ -276,6 +285,11 @@ void cli_tool_t::run(json11::Json cfg) // Allocate a new OSD number action_cb = start_alloc_osd(cfg); } + else if (cmd[0] == "simple-offsets") + { + // Calculate offsets for simple & stupid OSD deployment without superblock + action_cb = simple_offsets(cfg); + } else { fprintf(stderr, "unknown command: %s\n", cmd[0].string_value().c_str()); diff --git a/src/cli.h b/src/cli.h index 413eb0cf..3c1c10f7 100644 --- a/src/cli.h +++ b/src/cli.h @@ -58,4 +58,8 @@ public: std::function start_flatten(json11::Json); std::function start_snap_rm(json11::Json); std::function start_alloc_osd(json11::Json cfg, uint64_t *out = NULL); + std::function simple_offsets(json11::Json cfg); }; + +std::string format_size(uint64_t size); +uint64_t parse_size(std::string size_str); diff --git a/src/cli_create.cpp b/src/cli_create.cpp index 69e69e13..19251727 100644 --- a/src/cli_create.cpp +++ b/src/cli_create.cpp @@ -94,6 +94,11 @@ struct image_creator_t goto resume_2; else if (state == 3) goto resume_3; + if (!size) + { + fprintf(stderr, "Image size is missing\n"); + exit(1); + } for (auto & ic: parent->cli->st_cli.inode_config) { if (ic.second.name == image_name) @@ -432,6 +437,31 @@ resume_3: } }; +uint64_t parse_size(std::string size_str) +{ + uint64_t mul = 1; + char type_char = tolower(size_str[size_str.length()-1]); + if (type_char == 'k' || type_char == 'm' || type_char == 'g' || type_char == 't') + { + if (type_char == 'k') + mul = 1l<<10; + else if (type_char == 'm') + mul = 1l<<20; + else if (type_char == 'g') + mul = 1l<<30; + else /*if (type_char == 't')*/ + mul = 1l<<40; + size_str = size_str.substr(0, size_str.length()-1); + } + uint64_t size = json11::Json(size_str).uint64_value() * mul; + if (size == 0 && size_str != "0") + { + fprintf(stderr, "Invalid syntax for size: %s\n", size_str.c_str()); + exit(1); + } + return size; +} + std::function cli_tool_t::start_create(json11::Json cfg) { json11::Json::array cmd = cfg["command"].array_items(); @@ -458,33 +488,12 @@ std::function cli_tool_t::start_create(json11::Json cfg) image_creator->new_parent = cfg["parent"].string_value(); if (cfg["size"].string_value() != "") { - std::string size_str = cfg["size"].string_value(); - uint64_t mul = 1; - char type_char = tolower(size_str[size_str.length()-1]); - if (type_char == 'k' || type_char == 'm' || type_char == 'g' || type_char == 't') + image_creator->size = parse_size(cfg["size"].string_value()); + if (image_creator->size % 4096) { - if (type_char == 'k') - mul = 1l<<10; - else if (type_char == 'm') - mul = 1l<<20; - else if (type_char == 'g') - mul = 1l<<30; - else /*if (type_char == 't')*/ - mul = 1l<<40; - size_str = size_str.substr(0, size_str.length()-1); - } - uint64_t size = json11::Json(size_str).uint64_value() * mul; - if (size == 0) - { - fprintf(stderr, "Invalid syntax for size: %s\n", cfg["size"].string_value().c_str()); + fprintf(stderr, "Size should be a multiple of 4096\n"); exit(1); } - if (size % 4096) - { - fprintf(stderr, "Image size should be a multiple of 4096\n"); - exit(1); - } - image_creator->size = size; if (image_creator->new_snap != "") { fprintf(stderr, "--size can't be specified for snapshots\n"); diff --git a/src/cli_simple_offsets.cpp b/src/cli_simple_offsets.cpp new file mode 100644 index 00000000..09562f9d --- /dev/null +++ b/src/cli_simple_offsets.cpp @@ -0,0 +1,143 @@ +// Copyright (c) Vitaliy Filippov, 2019+ +// License: VNPL-1.1 (see README.md for details) + +#include +#include +#include +#include +#include "cli.h" +#include "cluster_client.h" +#include "base64.h" + +// Calculate offsets for a block device and print OSD command line parameters +std::function cli_tool_t::simple_offsets(json11::Json cfg) +{ + std::string device = cfg["command"][1].string_value(); + uint64_t object_size = parse_size(cfg["object_size"].string_value()); + uint64_t bitmap_granularity = parse_size(cfg["bitmap_granularity"].string_value()); + uint64_t journal_size = parse_size(cfg["journal_size"].string_value()); + uint64_t device_block_size = parse_size(cfg["device_block_size"].string_value()); + uint64_t journal_offset = parse_size(cfg["journal_offset"].string_value()); + uint64_t device_size = parse_size(cfg["device_size"].string_value()); + std::string format = cfg["format"].string_value(); + if (json_output) + format = "json"; + if (!object_size) + object_size = DEFAULT_BLOCK_SIZE; + if (!bitmap_granularity) + bitmap_granularity = DEFAULT_BITMAP_GRANULARITY; + if (!journal_size) + journal_size = 16*1024*1024; + if (!device_block_size) + device_block_size = 4096; + uint64_t orig_device_size = device_size; + if (!device_size) + { + struct stat st; + if (stat(device.c_str(), &st) < 0) + { + fprintf(stderr, "Can't stat %s: %s\n", device.c_str(), strerror(errno)); + exit(1); + } + if (S_ISBLK(st.st_mode)) + { + int fd = open(device.c_str(), O_DIRECT|O_RDONLY); + if (fd < 0 || ioctl(fd, BLKGETSIZE64, &device_size) < 0) + { + fprintf(stderr, "Failed to get device size for %s: %s\n", device.c_str(), strerror(errno)); + exit(1); + } + close(fd); + if (st.st_blksize < device_block_size) + { + fprintf( + stderr, "Warning: %s reports %lu byte blocks, but we use %lu." + " Set --device_block_size=%lu if you're sure it works well with %lu byte blocks.\n", + device.c_str(), st.st_blksize, device_block_size, st.st_blksize, st.st_blksize + ); + } + } + else if (S_ISREG(st.st_mode)) + { + device_size = st.st_size; + } + else + { + fprintf(stderr, "%s is neither a block device nor a regular file\n", device.c_str()); + exit(1); + } + } + if (!device_size) + { + fprintf(stderr, "Failed to get device size for %s\n", device.c_str()); + exit(1); + } + if (device_block_size < 512 || device_block_size > 1048576 || + device_block_size & (device_block_size-1) != 0) + { + fprintf(stderr, "Invalid device block size specified: %lu\n", device_block_size); + exit(1); + } + if (object_size < device_block_size || object_size > MAX_BLOCK_SIZE || + object_size & (object_size-1) != 0) + { + fprintf(stderr, "Invalid object size specified: %lu\n", object_size); + exit(1); + } + if (bitmap_granularity < device_block_size || bitmap_granularity > object_size || + bitmap_granularity & (bitmap_granularity-1) != 0) + { + fprintf(stderr, "Invalid bitmap granularity specified: %lu\n", bitmap_granularity); + exit(1); + } + journal_offset = ((journal_offset+device_block_size-1)/device_block_size)*device_block_size; + uint64_t meta_offset = journal_offset + ((journal_size+device_block_size-1)/device_block_size)*device_block_size; + uint64_t entries_per_block = (device_block_size / (24 + 2*object_size/bitmap_granularity/8)); + uint64_t object_count = ((device_size-meta_offset)/object_size); + uint64_t meta_size = (1 + (object_count+entries_per_block-1)/entries_per_block) * device_block_size; + uint64_t data_offset = meta_offset + meta_size; + if (format == "json") + { + // JSON + printf("%s\n", json11::Json(json11::Json::object { + { "meta_block_size", device_block_size }, + { "journal_block_size", device_block_size }, + { "data_size", device_size-data_offset }, + { "data_device", device }, + { "journal_offset", journal_offset }, + { "meta_offset", meta_offset }, + { "data_offset", data_offset }, + }).dump().c_str()); + } + else if (format == "env") + { + // Env + printf( + "meta_block_size=%lu\njournal_block_size=%lu\ndata_size=%lu\n" + "data_device=%s\njournal_offset=%lu\nmeta_offset=%lu\ndata_offset=%lu\n", + device_block_size, device_block_size, device_size-data_offset, + device.c_str(), journal_offset, meta_offset, data_offset + ); + } + else + { + // OSD command-line options + if (format != "options") + { + fprintf(stderr, "Metadata size: %s\nOptions for the OSD:\n", format_size(meta_size).c_str()); + } + if (device_block_size != 4096) + { + printf("--meta_block_size %lu\n--journal_block_size %lu\n", device_block_size, device_block_size); + } + if (orig_device_size) + { + printf("--data_size %lu\n", device_size-data_offset); + } + printf( + "--data_device %s\n--journal_offset %lu\n--meta_offset %lu\n--data_offset %lu\n", + device.c_str(), journal_offset, meta_offset, data_offset + ); + } + return NULL; +} diff --git a/src/cli_snap_rm.cpp b/src/cli_snap_rm.cpp index 8de5c012..23c15a03 100644 --- a/src/cli_snap_rm.cpp +++ b/src/cli_snap_rm.cpp @@ -1,6 +1,7 @@ // Copyright (c) Vitaliy Filippov, 2019+ // License: VNPL-1.1 (see README.md for details) +#include #include "cli.h" #include "cluster_client.h" #include "base64.h"