From a103065d127295ddef5243d1b986ba035a41118d Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Wed, 26 Mar 2025 01:52:08 +0300 Subject: [PATCH] Support multiple OSD networks and separate OSD cluster network --- docs/config/osd.en.md | 71 +++++++++++++---------- docs/config/osd.ru.md | 69 ++++++++++++---------- docs/config/src/osd.yml | 87 +++++++++++++++------------- src/client/etcd_state_client.cpp | 2 +- src/client/messenger.cpp | 98 +++++++++++++++++++++++++------- src/client/messenger.h | 19 +++++-- src/client/msgr_rdma.cpp | 16 ++---- src/client/msgr_rdma.h | 3 +- src/cmd/cli.h | 2 - src/cmd/cli_osd_tree.cpp | 1 + src/cmd/cli_pg_ls.cpp | 1 + src/cmd/cli_pool_create.cpp | 1 + src/cmd/cli_pool_ls.cpp | 17 +----- src/osd/osd.cpp | 65 ++++++++++----------- src/osd/osd.h | 5 +- src/osd/osd_cluster.cpp | 4 +- src/util/addr_util.cpp | 8 +-- src/util/addr_util.h | 2 +- src/util/json_util.cpp | 16 ++++++ src/util/json_util.h | 1 + 20 files changed, 288 insertions(+), 200 deletions(-) diff --git a/docs/config/osd.en.md b/docs/config/osd.en.md index a3035958..d138ed03 100644 --- a/docs/config/osd.en.md +++ b/docs/config/osd.en.md @@ -10,13 +10,14 @@ These parameters only apply to OSDs, are not fixed at the moment of OSD drive initialization and can be changed - in /etc/vitastor/vitastor.conf or [vitastor-disk update-sb](../usage/disk.en.md#update-sb) with an OSD restart or, for some of them, even without restarting by updating configuration in etcd. +- [osd_network](#osd_network) +- [osd_cluster_network](#osd_cluster_network) +- [bind_address](#bind_address) +- [bind_port](#bind_port) - [osd_iothread_count](#osd_iothread_count) - [etcd_report_interval](#etcd_report_interval) - [etcd_stats_interval](#etcd_stats_interval) - [run_primary](#run_primary) -- [osd_network](#osd_network) -- [bind_address](#bind_address) -- [bind_port](#bind_port) - [autosync_interval](#autosync_interval) - [autosync_writes](#autosync_writes) - [recovery_queue_depth](#recovery_queue_depth) @@ -65,6 +66,42 @@ with an OSD restart or, for some of them, even without restarting by updating co - [min_discard_size](#min_discard_size) - [allow_net_split](#allow_net_split) +## osd_network + +- Type: string or array of strings + +Network mask of public OSD network(s) (IPv4 or IPv6). Each OSD listens on all +addresses of UP + RUNNING interfaces matching one of these networks, on the +same port. Port is auto-selected except if [bind_port](#bind_port) is +explicitly specified. Bind address(es) may also be overridden manually by +specifying [bind_address](#bind_address). If OSD networks are not specified +at all, OSD just listens on a wildcard address (0.0.0.0). + +## osd_cluster_network + +- Type: string or array of strings + +Network mask of separate network(s) (IPv4 or IPv6) to use for OSD +cluster connections. I.e. OSDs will always attempt to use these networks +to connect to other OSDs, while clients will attempt to use networks from +[osd_network](#osd_network). + +## bind_address + +- Type: string or array of strings + +Instead of the network mask, you can also set OSD listen addresses explicitly +using this parameter. May be useful if you want to start OSDs on interfaces +that are not UP + RUNNING. + +## bind_port + +- Type: integer + +By default, OSDs pick random ports to use for incoming connections +automatically. With this option you can set a specific port for a specific +OSD by hand. + ## osd_iothread_count - Type: integer @@ -107,34 +144,6 @@ debugging purposes. It's possible to implement additional feature for the monitor which may allow to separate primary and secondary OSDs, but it's unclear why anyone could need it, so it's not implemented. -## osd_network - -- Type: string or array of strings - -Network mask of the network (IPv4 or IPv6) to use for OSDs. Note that -although it's possible to specify multiple networks here, this does not -mean that OSDs will create multiple listening sockets - they'll only -pick the first matching address of an UP + RUNNING interface. Separate -networks for cluster and client connections are also not implemented, but -they are mostly useless anyway, so it's not a big deal. - -## bind_address - -- Type: string -- Default: 0.0.0.0 - -Instead of the network mask, you can also set OSD listen address explicitly -using this parameter. May be useful if you want to start OSDs on interfaces -that are not UP + RUNNING. - -## bind_port - -- Type: integer - -By default, OSDs pick random ports to use for incoming connections -automatically. With this option you can set a specific port for a specific -OSD by hand. - ## autosync_interval - Type: seconds diff --git a/docs/config/osd.ru.md b/docs/config/osd.ru.md index 504f0486..37ef79d5 100644 --- a/docs/config/osd.ru.md +++ b/docs/config/osd.ru.md @@ -11,13 +11,14 @@ момент с перезапуском OSD в /etc/vitastor/vitastor.conf или [vitastor-disk update-sb](../usage/disk.ru.md#update-sb), а некоторые и без перезапуска, с помощью изменения конфигурации в etcd. +- [osd_network](#osd_network) +- [osd_cluster_network](#osd_cluster_network) +- [bind_address](#bind_address) +- [bind_port](#bind_port) - [osd_iothread_count](#osd_iothread_count) - [etcd_report_interval](#etcd_report_interval) - [etcd_stats_interval](#etcd_stats_interval) - [run_primary](#run_primary) -- [osd_network](#osd_network) -- [bind_address](#bind_address) -- [bind_port](#bind_port) - [autosync_interval](#autosync_interval) - [autosync_writes](#autosync_writes) - [recovery_queue_depth](#recovery_queue_depth) @@ -66,6 +67,40 @@ - [min_discard_size](#min_discard_size) - [allow_net_split](#allow_net_split) +## osd_network + +- Тип: строка или массив строк + +Маски подсетей (IPv4 или IPv6) публичной сети или сетей OSD. Каждый OSD слушает +один и тот же порт на всех адресах поднятых (UP + RUNNING) сетевых интерфейсов, +соответствующих одной из указанных сетей. Порт выбирается автоматически, если +только [bind_port](#bind_port) не задан явно. Адреса для подключений можно +также переопределить явно, задав [bind_address](#bind_address). Если сети OSD +не заданы вообще, OSD слушает все адреса (0.0.0.0). + +## osd_cluster_network + +- Тип: строка или массив строк + +Маски подсетей (IPv4 или IPv6) отдельной кластерной сети или сетей OSD. +То есть, OSD будут всегда стараться использовать эти сети для соединений +с другими OSD, а клиенты будут стараться использовать сети из [osd_network](#osd_network). + +## bind_address + +- Тип: строка или массив строк + +Этим параметром можно явным образом задать адрес(а), на котором будет ожидать +соединений OSD (вместо использования маски подсети). Может быть полезно, +например, чтобы запускать OSD на неподнятых интерфейсах (не UP + RUNNING). + +## bind_port + +- Тип: целое число + +По умолчанию OSD сами выбирают случайные порты для входящих подключений. +С помощью данной опции вы можете задать порт для отдельного OSD вручную. + ## osd_iothread_count - Тип: целое число @@ -110,34 +145,6 @@ max_etcd_attempts * etcd_quick_timeout. первичные OSD от вторичных, но пока не понятно, зачем это может кому-то понадобиться, поэтому это не реализовано. -## osd_network - -- Тип: строка или массив строк - -Маска подсети (IPv4 или IPv6) для использования для соединений с OSD. -Имейте в виду, что хотя сейчас и можно передать в этот параметр несколько -подсетей, это не означает, что OSD будут создавать несколько слушающих -сокетов - они лишь будут выбирать адрес первого поднятого (состояние UP + -RUNNING), подходящий под заданную маску. Также не реализовано разделение -кластерной и публичной сетей OSD. Правда, от него обычно всё равно довольно -мало толку, так что особенной проблемы в этом нет. - -## bind_address - -- Тип: строка -- Значение по умолчанию: 0.0.0.0 - -Этим параметром можно явным образом задать адрес, на котором будет ожидать -соединений OSD (вместо использования маски подсети). Может быть полезно, -например, чтобы запускать OSD на неподнятых интерфейсах (не UP + RUNNING). - -## bind_port - -- Тип: целое число - -По умолчанию OSD сами выбирают случайные порты для входящих подключений. -С помощью данной опции вы можете задать порт для отдельного OSD вручную. - ## autosync_interval - Тип: секунды diff --git a/docs/config/src/osd.yml b/docs/config/src/osd.yml index f707be44..a88c1101 100644 --- a/docs/config/src/osd.yml +++ b/docs/config/src/osd.yml @@ -1,3 +1,52 @@ +- name: osd_network + type: string or array of strings + type_ru: строка или массив строк + info: | + Network mask of public OSD network(s) (IPv4 or IPv6). Each OSD listens on all + addresses of UP + RUNNING interfaces matching one of these networks, on the + same port. Port is auto-selected except if [bind_port](#bind_port) is + explicitly specified. Bind address(es) may also be overridden manually by + specifying [bind_address](#bind_address). If OSD networks are not specified + at all, OSD just listens on a wildcard address (0.0.0.0). + info_ru: | + Маски подсетей (IPv4 или IPv6) публичной сети или сетей OSD. Каждый OSD слушает + один и тот же порт на всех адресах поднятых (UP + RUNNING) сетевых интерфейсов, + соответствующих одной из указанных сетей. Порт выбирается автоматически, если + только [bind_port](#bind_port) не задан явно. Адреса для подключений можно + также переопределить явно, задав [bind_address](#bind_address). Если сети OSD + не заданы вообще, OSD слушает все адреса (0.0.0.0). +- name: osd_cluster_network + type: string or array of strings + type_ru: строка или массив строк + info: | + Network mask of separate network(s) (IPv4 or IPv6) to use for OSD + cluster connections. I.e. OSDs will always attempt to use these networks + to connect to other OSDs, while clients will attempt to use networks from + [osd_network](#osd_network). + info_ru: | + Маски подсетей (IPv4 или IPv6) отдельной кластерной сети или сетей OSD. + То есть, OSD будут всегда стараться использовать эти сети для соединений + с другими OSD, а клиенты будут стараться использовать сети из [osd_network](#osd_network). +- name: bind_address + type: string or array of strings + type_ru: строка или массив строк + info: | + Instead of the network mask, you can also set OSD listen addresses explicitly + using this parameter. May be useful if you want to start OSDs on interfaces + that are not UP + RUNNING. + info_ru: | + Этим параметром можно явным образом задать адрес(а), на котором будет ожидать + соединений OSD (вместо использования маски подсети). Может быть полезно, + например, чтобы запускать OSD на неподнятых интерфейсах (не UP + RUNNING). +- name: bind_port + type: int + info: | + By default, OSDs pick random ports to use for incoming connections + automatically. With this option you can set a specific port for a specific + OSD by hand. + info_ru: | + По умолчанию OSD сами выбирают случайные порты для входящих подключений. + С помощью данной опции вы можете задать порт для отдельного OSD вручную. - name: osd_iothread_count type: int default: 0 @@ -56,44 +105,6 @@ реализовать дополнительный режим для монитора, который позволит отделять первичные OSD от вторичных, но пока не понятно, зачем это может кому-то понадобиться, поэтому это не реализовано. -- name: osd_network - type: string or array of strings - type_ru: строка или массив строк - info: | - Network mask of the network (IPv4 or IPv6) to use for OSDs. Note that - although it's possible to specify multiple networks here, this does not - mean that OSDs will create multiple listening sockets - they'll only - pick the first matching address of an UP + RUNNING interface. Separate - networks for cluster and client connections are also not implemented, but - they are mostly useless anyway, so it's not a big deal. - info_ru: | - Маска подсети (IPv4 или IPv6) для использования для соединений с OSD. - Имейте в виду, что хотя сейчас и можно передать в этот параметр несколько - подсетей, это не означает, что OSD будут создавать несколько слушающих - сокетов - они лишь будут выбирать адрес первого поднятого (состояние UP + - RUNNING), подходящий под заданную маску. Также не реализовано разделение - кластерной и публичной сетей OSD. Правда, от него обычно всё равно довольно - мало толку, так что особенной проблемы в этом нет. -- name: bind_address - type: string - default: "0.0.0.0" - info: | - Instead of the network mask, you can also set OSD listen address explicitly - using this parameter. May be useful if you want to start OSDs on interfaces - that are not UP + RUNNING. - info_ru: | - Этим параметром можно явным образом задать адрес, на котором будет ожидать - соединений OSD (вместо использования маски подсети). Может быть полезно, - например, чтобы запускать OSD на неподнятых интерфейсах (не UP + RUNNING). -- name: bind_port - type: int - info: | - By default, OSDs pick random ports to use for incoming connections - automatically. With this option you can set a specific port for a specific - OSD by hand. - info_ru: | - По умолчанию OSD сами выбирают случайные порты для входящих подключений. - С помощью данной опции вы можете задать порт для отдельного OSD вручную. - name: autosync_interval type: sec default: 5 diff --git a/src/client/etcd_state_client.cpp b/src/client/etcd_state_client.cpp index 4cb9507a..0a5addcd 100644 --- a/src/client/etcd_state_client.cpp +++ b/src/client/etcd_state_client.cpp @@ -182,7 +182,7 @@ void etcd_state_client_t::add_etcd_url(std::string addr) exit(1); } if (!local_ips.size()) - local_ips = getifaddr_list(std::vector(), true); + local_ips = getifaddr_list(std::vector(), true); std::string check_addr; int pos = addr.find('/'); int pos2 = addr.find(':'); diff --git a/src/client/messenger.cpp b/src/client/messenger.cpp index cc6ce005..5f588ccb 100644 --- a/src/client/messenger.cpp +++ b/src/client/messenger.cpp @@ -117,11 +117,13 @@ void msgr_iothread_t::run() void osd_messenger_t::init() { + // FIXME: Support multiple RDMA networks?! #ifdef WITH_RDMA if (use_rdma) { rdma_context = msgr_rdma_context_t::create( - osd_networks, rdma_device != "" ? rdma_device.c_str() : NULL, + osd_num && osd_cluster_network_masks.size() ? osd_cluster_network_masks : osd_network_masks, + rdma_device != "" ? rdma_device.c_str() : NULL, rdma_port_num, rdma_gid_index, rdma_mtu, rdma_odp, log_level ); if (!rdma_context) @@ -282,15 +284,6 @@ void osd_messenger_t::parse_config(const json11::Json & config) if (!this->rdma_max_msg || this->rdma_max_msg > 128*1024*1024) this->rdma_max_msg = 129*1024; this->rdma_odp = config["rdma_odp"].bool_value(); - std::vector mask; - if (config["bind_address"].is_string()) - mask.push_back(config["bind_address"].string_value()); - else if (config["osd_network"].is_string()) - mask.push_back(config["osd_network"].string_value()); - else - for (auto v: config["osd_network"].array_items()) - mask.push_back(v.string_value()); - this->osd_networks = mask; #endif if (!osd_num) this->iothread_count = (uint32_t)config["client_iothread_count"].uint64_value(); @@ -314,23 +307,86 @@ void osd_messenger_t::parse_config(const json11::Json & config) if (!this->osd_ping_timeout) this->osd_ping_timeout = 5; this->log_level = config["log_level"].uint64_value(); + // OSD public & cluster networks + this->osd_networks.clear(); + if (config["osd_network"].is_string()) + this->osd_networks.push_back(config["osd_network"].string_value()); + else + for (auto v: config["osd_network"].array_items()) + this->osd_networks.push_back(v.string_value()); + this->osd_cluster_networks.clear(); + if (config["osd_cluster_network"].is_string()) + this->osd_cluster_networks.push_back(config["osd_cluster_network"].string_value()); + else + for (auto v: config["osd_cluster_network"].array_items()) + this->osd_cluster_networks.push_back(v.string_value()); + if (this->osd_cluster_networks.size()) + for (auto & net: this->osd_cluster_networks) + for (int i = this->osd_networks.size()-1; i >= 0; i--) + if (this->osd_networks[i] == net) + this->osd_networks.erase(this->osd_networks.begin()+i, this->osd_networks.begin()+i+1); + this->osd_network_masks.clear(); + for (auto & netstr: this->osd_networks) + this->osd_network_masks.push_back(cidr_parse(netstr)); + this->osd_cluster_network_masks.clear(); + for (auto & netstr: this->osd_cluster_networks) + this->osd_cluster_network_masks.push_back(cidr_parse(netstr)); + this->all_osd_networks.clear(); + this->all_osd_networks.insert(this->all_osd_networks.end(), this->osd_networks.begin(), this->osd_networks.end()); + this->all_osd_networks.insert(this->all_osd_networks.end(), this->osd_cluster_networks.begin(), this->osd_cluster_networks.end()); + this->all_osd_network_masks.clear(); + this->all_osd_network_masks.insert(this->all_osd_network_masks.end(), this->osd_network_masks.begin(), this->osd_network_masks.end()); + this->all_osd_network_masks.insert(this->all_osd_network_masks.end(), this->osd_cluster_network_masks.begin(), this->osd_cluster_network_masks.end()); + if (!this->osd_networks.size()) + { + this->osd_networks = this->osd_cluster_networks; + this->osd_network_masks = this->osd_cluster_network_masks; + } } void osd_messenger_t::connect_peer(uint64_t peer_osd, json11::Json peer_state) { - if (wanted_peers.find(peer_osd) == wanted_peers.end()) + if (wanted_peers[peer_osd].raw_address_list != peer_state["addresses"]) { - wanted_peers[peer_osd] = (osd_wanted_peer_t){ - .address_list = peer_state["addresses"], - .port = (int)peer_state["port"].int64_value(), - }; + wanted_peers[peer_osd].raw_address_list = peer_state["addresses"]; + // We are an OSD -> try to select a cluster address + // We are a client -> try to select a public address + // OSD only has 1 address -> don't try anything, it's pointless + // FIXME: Maybe support optional fallback from cluster to public network? + auto & match_masks = (this->osd_num ? osd_cluster_network_masks : osd_network_masks); + if (peer_state["addresses"].array_items().size() > 1 && match_masks.size()) + { + json11::Json::array address_list; + for (auto json_addr: peer_state["addresses"].array_items()) + { + struct sockaddr_storage addr; + auto ok = string_to_addr(json_addr.string_value(), false, 0, &addr); + if (ok) + { + bool matches = false; + for (auto & mask: match_masks) + { + if (mask.family == addr.ss_family && (mask.family == AF_INET + ? cidr_match(*(in_addr*)&addr, mask.ipv4, mask.bits) + : cidr6_match(*(in6_addr*)&addr, mask.ipv6, mask.bits))) + { + matches = true; + break; + } + } + if (matches) + address_list.push_back(json_addr); + } + } + if (!address_list.size()) + address_list = peer_state["addresses"].array_items(); + wanted_peers[peer_osd].address_list = address_list; + } + else + wanted_peers[peer_osd].address_list = peer_state["addresses"]; + wanted_peers[peer_osd].address_changed = true; } - else - { - wanted_peers[peer_osd].address_list = peer_state["addresses"]; - wanted_peers[peer_osd].port = (int)peer_state["port"].int64_value(); - } - wanted_peers[peer_osd].address_changed = true; + wanted_peers[peer_osd].port = (int)peer_state["port"].int64_value(); try_connect_peer(peer_osd); } diff --git a/src/client/messenger.h b/src/client/messenger.h index e296b7e1..574f2946 100644 --- a/src/client/messenger.h +++ b/src/client/messenger.h @@ -16,6 +16,7 @@ #include "json11/json11.hpp" #include "msgr_op.h" #include "timerfd_manager.h" +#include "addr_util.h" #include #define CL_READ_HDR 1 @@ -93,13 +94,14 @@ struct osd_client_t struct osd_wanted_peer_t { + json11::Json raw_address_list; json11::Json address_list; - int port; - time_t last_connect_attempt; - bool connecting, address_changed; - int address_index; + int port = 0; + time_t last_connect_attempt = 0; + bool connecting = false, address_changed = false; + int address_index = 0; std::string cur_addr; - int cur_port; + int cur_port = 0; }; struct osd_op_stats_t @@ -165,7 +167,6 @@ protected: #ifdef WITH_RDMA bool use_rdma = true; - std::vector osd_networks; std::string rdma_device; uint64_t rdma_port_num = 1, rdma_mtu = 0; int rdma_gid_index = -1; @@ -190,6 +191,12 @@ public: std::map clients; std::map wanted_peers; std::map osd_peer_fds; + std::vector osd_networks; + std::vector osd_network_masks; + std::vector osd_cluster_networks; + std::vector osd_cluster_network_masks; + std::vector all_osd_networks; + std::vector all_osd_network_masks; // op statistics osd_op_stats_t stats, recovery_stats; diff --git a/src/client/msgr_rdma.cpp b/src/client/msgr_rdma.cpp index 1039f21d..15416ebe 100644 --- a/src/client/msgr_rdma.cpp +++ b/src/client/msgr_rdma.cpp @@ -3,7 +3,6 @@ #include #include -#include "addr_util.h" #include "msgr_rdma.h" #include "messenger.h" @@ -77,7 +76,7 @@ static bool is_ipv4_gid(ibv_gid_entry *gidx) ((uint32_t*)gidx->gid.raw)[2] == 0xffff0000); } -static bool match_gid(ibv_gid_entry *gidx, addr_mask_t *networks, int nnet) +static bool match_gid(ibv_gid_entry *gidx, const addr_mask_t *networks, int nnet) { if (gidx->gid_type != IBV_GID_TYPE_ROCE_V1 && gidx->gid_type != IBV_GID_TYPE_ROCE_V2 || @@ -125,7 +124,7 @@ static void log_rdma_dev_port_gid(ibv_device *dev, int ib_port, int gid_index, i ); } -static matched_dev match_device(ibv_device **dev_list, addr_mask_t *networks, int nnet, int log_level) +static matched_dev match_device(ibv_device **dev_list, const addr_mask_t *networks, int nnet, int log_level) { matched_dev best; ibv_device_attr attr; @@ -201,7 +200,7 @@ cleanup: } #endif -msgr_rdma_context_t *msgr_rdma_context_t::create(std::vector osd_networks, const char *ib_devname, uint8_t ib_port, int gid_index, uint32_t mtu, bool odp, int log_level) +msgr_rdma_context_t *msgr_rdma_context_t::create(const std::vector & osd_network_masks, const char *ib_devname, uint8_t ib_port, int gid_index, uint32_t mtu, bool odp, int log_level) { int res; ibv_device **dev_list = NULL; @@ -242,14 +241,9 @@ msgr_rdma_context_t *msgr_rdma_context_t::create(std::vector osd_ne } } #ifdef IBV_ADVISE_MR_ADVICE_PREFETCH_NO_FAULT - else if (osd_networks.size()) + else if (osd_network_masks.size()) { - std::vector nets; - for (auto & netstr: osd_networks) - { - nets.push_back(cidr_parse(netstr)); - } - auto best = match_device(dev_list, nets.data(), nets.size(), log_level); + auto best = match_device(dev_list, osd_network_masks.data(), osd_network_masks.size(), log_level); if (best.dev == -2) { best.dev = 0; diff --git a/src/client/msgr_rdma.h b/src/client/msgr_rdma.h index cfa3cdf7..065eead6 100644 --- a/src/client/msgr_rdma.h +++ b/src/client/msgr_rdma.h @@ -5,6 +5,7 @@ #include #include #include +#include "addr_util.h" struct msgr_rdma_address_t { @@ -36,7 +37,7 @@ struct msgr_rdma_context_t int max_cqe = 0; int used_max_cqe = 0; - static msgr_rdma_context_t *create(std::vector osd_networks, const char *ib_devname, uint8_t ib_port, int gid_index, uint32_t mtu, bool odp, int log_level); + static msgr_rdma_context_t *create(const std::vector & osd_network_masks, const char *ib_devname, uint8_t ib_port, int gid_index, uint32_t mtu, bool odp, int log_level); ~msgr_rdma_context_t(); }; diff --git a/src/cmd/cli.h b/src/cmd/cli.h index cf022e85..d8e67312 100644 --- a/src/cmd/cli.h +++ b/src/cmd/cli.h @@ -98,5 +98,3 @@ std::string format_lat(uint64_t lat); std::string format_q(double depth); bool stupid_glob(const std::string str, const std::string glob); - -std::string implode(const std::string & sep, json11::Json array); diff --git a/src/cmd/cli_osd_tree.cpp b/src/cmd/cli_osd_tree.cpp index 96356907..d3ccc43d 100644 --- a/src/cmd/cli_osd_tree.cpp +++ b/src/cmd/cli_osd_tree.cpp @@ -7,6 +7,7 @@ #include "epoll_manager.h" #include "pg_states.h" #include "str_util.h" +#include "json_util.h" struct placement_osd_t { diff --git a/src/cmd/cli_pg_ls.cpp b/src/cmd/cli_pg_ls.cpp index 8eb24794..ce03fe16 100644 --- a/src/cmd/cli_pg_ls.cpp +++ b/src/cmd/cli_pg_ls.cpp @@ -5,6 +5,7 @@ #include "cluster_client.h" #include "pg_states.h" #include "str_util.h" +#include "json_util.h" struct pg_lister_t { diff --git a/src/cmd/cli_pool_create.cpp b/src/cmd/cli_pool_create.cpp index d09ec328..52641479 100644 --- a/src/cmd/cli_pool_create.cpp +++ b/src/cmd/cli_pool_create.cpp @@ -10,6 +10,7 @@ #include "epoll_manager.h" #include "pg_states.h" #include "str_util.h" +#include "json_util.h" struct pool_creator_t { diff --git a/src/cmd/cli_pool_ls.cpp b/src/cmd/cli_pool_ls.cpp index 7a1efb7a..5b236813 100644 --- a/src/cmd/cli_pool_ls.cpp +++ b/src/cmd/cli_pool_ls.cpp @@ -5,6 +5,7 @@ #include "cli.h" #include "cluster_client.h" #include "str_util.h" +#include "json_util.h" #include "pg_states.h" // List pools with space statistics @@ -665,19 +666,3 @@ std::function cli_tool_t::start_pool_ls(json11::Json cfg) return false; }; } - -std::string implode(const std::string & sep, json11::Json array) -{ - if (array.is_number() || array.is_bool() || array.is_string()) - { - return array.as_string(); - } - std::string res; - bool first = true; - for (auto & item: array.array_items()) - { - res += (first ? item.as_string() : sep+item.as_string()); - first = false; - } - return res; -} diff --git a/src/osd/osd.cpp b/src/osd/osd.cpp index 255d1bb9..6a884d3b 100644 --- a/src/osd/osd.cpp +++ b/src/osd/osd.cpp @@ -120,7 +120,9 @@ osd_t::~osd_t() delete epmgr; if (bs) delete bs; - close(listen_fd); + for (auto listen_fd: listen_fds) + close(listen_fd); + listen_fds.clear(); free(zero_buffer); } @@ -162,9 +164,15 @@ void osd_t::parse_config(bool init) else immediate_commit = IMMEDIATE_NONE; // Bind address - bind_address = config["bind_address"].string_value(); - if (bind_address == "") - bind_address = "0.0.0.0"; + cfg_bind_addresses.clear(); + if (config.find("bind_address") != config.end()) + { + if (config["bind_address"].is_string()) + cfg_bind_addresses.push_back(config["bind_address"].string_value()); + else if (config["bind_address"].is_array()) + for (auto & addr: config["bind_address"].array_items()) + cfg_bind_addresses.push_back(addr.string_value()); + } bind_port = config["bind_port"].uint64_value(); if (bind_port <= 0 || bind_port > 65535) bind_port = 0; @@ -322,41 +330,34 @@ void osd_t::parse_config(bool init) void osd_t::bind_socket() { - if (config["osd_network"].is_string() || - config["osd_network"].is_array()) + if (cfg_bind_addresses.size()) { - std::vector mask; - if (config["osd_network"].is_string()) - mask.push_back(config["osd_network"].string_value()); - else - for (auto v: config["osd_network"].array_items()) - mask.push_back(v.string_value()); - auto matched_addrs = getifaddr_list(mask); - if (matched_addrs.size() > 1) + bind_addresses = cfg_bind_addresses; + } + else if (msgr.all_osd_network_masks.size()) + { + bind_addresses = getifaddr_list(msgr.all_osd_network_masks); + if (!bind_addresses.size()) { - fprintf(stderr, "More than 1 address matches requested network(s): %s\n", json11::Json(matched_addrs).dump().c_str()); - force_stop(1); - } - if (!matched_addrs.size()) - { - std::string nets; - for (auto v: mask) - nets += (nets == "" ? v : ","+v); + auto nets = implode(", ", msgr.all_osd_networks); fprintf(stderr, "Addresses matching osd_network(s) %s not found\n", nets.c_str()); force_stop(1); } - bind_address = matched_addrs[0]; } - - // FIXME Support multiple listening sockets - - listen_fd = create_and_bind_socket(bind_address, bind_port, listen_backlog, &listening_port); - fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL, 0) | O_NONBLOCK); - - epmgr->set_fd_handler(listen_fd, false, [this](int fd, int events) + else { - msgr.accept_connections(listen_fd); - }); + bind_addresses.push_back("0.0.0.0"); + } + for (auto & bind_address: bind_addresses) + { + int listen_fd = create_and_bind_socket(bind_address, listening_port ? listening_port : bind_port, listen_backlog, &listening_port); + fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL, 0) | O_NONBLOCK); + epmgr->set_fd_handler(listen_fd, false, [this](int fd, int events) + { + msgr.accept_connections(fd); + }); + listen_fds.push_back(listen_fd); + } } bool osd_t::shutdown() diff --git a/src/osd/osd.h b/src/osd/osd.h index 274fea0f..cb51c21d 100644 --- a/src/osd/osd.h +++ b/src/osd/osd.h @@ -107,7 +107,7 @@ class osd_t bool no_recovery = false; bool no_scrub = false; bool allow_net_split = false; - std::string bind_address; + std::vector cfg_bind_addresses; int bind_port, listen_backlog = 128; // FIXME: Implement client queue depth limit int client_queue_depth = 128; @@ -200,7 +200,8 @@ class osd_t epoll_manager_t *epmgr = NULL; int listening_port = 0; - int listen_fd = 0; + std::vector bind_addresses; + std::vector listen_fds; ring_consumer_t consumer; // op statistics diff --git a/src/osd/osd_cluster.cpp b/src/osd/osd_cluster.cpp index a9354a43..ab6cdfab 100644 --- a/src/osd/osd_cluster.cpp +++ b/src/osd/osd_cluster.cpp @@ -165,8 +165,8 @@ json11::Json osd_t::get_osd_state() hostname.resize(strnlen(hostname.data(), hostname.size())); json11::Json::object st; st["state"] = "up"; - if (bind_address != "0.0.0.0") - st["addresses"] = json11::Json::array { bind_address }; + if (bind_addresses.size() != 1 || bind_addresses[0] != "0.0.0.0") + st["addresses"] = bind_addresses; else st["addresses"] = getifaddr_list(); st["host"] = std::string(hostname.data(), hostname.size()); diff --git a/src/util/addr_util.cpp b/src/util/addr_util.cpp index 84474588..c3856b65 100644 --- a/src/util/addr_util.cpp +++ b/src/util/addr_util.cpp @@ -126,13 +126,11 @@ addr_mask_t cidr_parse(std::string mask) } } -std::vector getifaddr_list(std::vector mask_cfg, bool include_v6) +std::vector getifaddr_list(const std::vector & masks, bool include_v6) { - std::vector masks; - for (auto mask: mask_cfg) + for (auto & mask: masks) { - masks.push_back(cidr_parse(mask)); - if (masks[masks.size()-1].family == AF_INET6) + if (mask.family == AF_INET6) { // Auto-enable IPv6 addresses include_v6 = true; diff --git a/src/util/addr_util.h b/src/util/addr_util.h index 034733cf..d694a723 100644 --- a/src/util/addr_util.h +++ b/src/util/addr_util.h @@ -18,5 +18,5 @@ std::string addr_to_string(const sockaddr_storage &addr); addr_mask_t cidr_parse(std::string mask); bool cidr_match(const in_addr &address, const in_addr &network, uint8_t bits); bool cidr6_match(const in6_addr &address, const in6_addr &network, uint8_t bits); -std::vector getifaddr_list(std::vector mask_cfg = std::vector(), bool include_v6 = false); +std::vector getifaddr_list(const std::vector & masks = std::vector(), bool include_v6 = false); int create_and_bind_socket(std::string bind_address, int bind_port, int listen_backlog, int *listening_port); diff --git a/src/util/json_util.cpp b/src/util/json_util.cpp index f329a23e..e8c96d8f 100644 --- a/src/util/json_util.cpp +++ b/src/util/json_util.cpp @@ -33,3 +33,19 @@ bool json_is_false(const json11::Json & val) return !val.bool_value(); return false; } + +std::string implode(const std::string & sep, json11::Json array) +{ + if (array.is_number() || array.is_bool() || array.is_string()) + { + return array.as_string(); + } + std::string res; + bool first = true; + for (auto & item: array.array_items()) + { + res += (first ? item.as_string() : sep+item.as_string()); + first = false; + } + return res; +} diff --git a/src/util/json_util.h b/src/util/json_util.h index cd958d95..7594860d 100644 --- a/src/util/json_util.h +++ b/src/util/json_util.h @@ -11,3 +11,4 @@ std::map json_to_string_map(const json11::Json::object & config); bool json_is_true(const json11::Json & val); bool json_is_false(const json11::Json & val); +std::string implode(const std::string & sep, json11::Json array);