Support multiple OSD networks and separate OSD cluster network
This commit is contained in:
+40
-31
@@ -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)
|
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.
|
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)
|
- [osd_iothread_count](#osd_iothread_count)
|
||||||
- [etcd_report_interval](#etcd_report_interval)
|
- [etcd_report_interval](#etcd_report_interval)
|
||||||
- [etcd_stats_interval](#etcd_stats_interval)
|
- [etcd_stats_interval](#etcd_stats_interval)
|
||||||
- [run_primary](#run_primary)
|
- [run_primary](#run_primary)
|
||||||
- [osd_network](#osd_network)
|
|
||||||
- [bind_address](#bind_address)
|
|
||||||
- [bind_port](#bind_port)
|
|
||||||
- [autosync_interval](#autosync_interval)
|
- [autosync_interval](#autosync_interval)
|
||||||
- [autosync_writes](#autosync_writes)
|
- [autosync_writes](#autosync_writes)
|
||||||
- [recovery_queue_depth](#recovery_queue_depth)
|
- [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)
|
- [min_discard_size](#min_discard_size)
|
||||||
- [allow_net_split](#allow_net_split)
|
- [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
|
## osd_iothread_count
|
||||||
|
|
||||||
- Type: integer
|
- 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
|
monitor which may allow to separate primary and secondary OSDs, but it's
|
||||||
unclear why anyone could need it, so it's not implemented.
|
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
|
## autosync_interval
|
||||||
|
|
||||||
- Type: seconds
|
- Type: seconds
|
||||||
|
|||||||
+38
-31
@@ -11,13 +11,14 @@
|
|||||||
момент с перезапуском OSD в /etc/vitastor/vitastor.conf или [vitastor-disk update-sb](../usage/disk.ru.md#update-sb),
|
момент с перезапуском OSD в /etc/vitastor/vitastor.conf или [vitastor-disk update-sb](../usage/disk.ru.md#update-sb),
|
||||||
а некоторые и без перезапуска, с помощью изменения конфигурации в etcd.
|
а некоторые и без перезапуска, с помощью изменения конфигурации в 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)
|
- [osd_iothread_count](#osd_iothread_count)
|
||||||
- [etcd_report_interval](#etcd_report_interval)
|
- [etcd_report_interval](#etcd_report_interval)
|
||||||
- [etcd_stats_interval](#etcd_stats_interval)
|
- [etcd_stats_interval](#etcd_stats_interval)
|
||||||
- [run_primary](#run_primary)
|
- [run_primary](#run_primary)
|
||||||
- [osd_network](#osd_network)
|
|
||||||
- [bind_address](#bind_address)
|
|
||||||
- [bind_port](#bind_port)
|
|
||||||
- [autosync_interval](#autosync_interval)
|
- [autosync_interval](#autosync_interval)
|
||||||
- [autosync_writes](#autosync_writes)
|
- [autosync_writes](#autosync_writes)
|
||||||
- [recovery_queue_depth](#recovery_queue_depth)
|
- [recovery_queue_depth](#recovery_queue_depth)
|
||||||
@@ -66,6 +67,40 @@
|
|||||||
- [min_discard_size](#min_discard_size)
|
- [min_discard_size](#min_discard_size)
|
||||||
- [allow_net_split](#allow_net_split)
|
- [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
|
## osd_iothread_count
|
||||||
|
|
||||||
- Тип: целое число
|
- Тип: целое число
|
||||||
@@ -110,34 +145,6 @@ max_etcd_attempts * etcd_quick_timeout.
|
|||||||
первичные OSD от вторичных, но пока не понятно, зачем это может кому-то
|
первичные 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
|
## autosync_interval
|
||||||
|
|
||||||
- Тип: секунды
|
- Тип: секунды
|
||||||
|
|||||||
+49
-38
@@ -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
|
- name: osd_iothread_count
|
||||||
type: int
|
type: int
|
||||||
default: 0
|
default: 0
|
||||||
@@ -56,44 +105,6 @@
|
|||||||
реализовать дополнительный режим для монитора, который позволит отделять
|
реализовать дополнительный режим для монитора, который позволит отделять
|
||||||
первичные OSD от вторичных, но пока не понятно, зачем это может кому-то
|
первичные 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
|
- name: autosync_interval
|
||||||
type: sec
|
type: sec
|
||||||
default: 5
|
default: 5
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ void etcd_state_client_t::add_etcd_url(std::string addr)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (!local_ips.size())
|
if (!local_ips.size())
|
||||||
local_ips = getifaddr_list(std::vector<std::string>(), true);
|
local_ips = getifaddr_list(std::vector<addr_mask_t>(), true);
|
||||||
std::string check_addr;
|
std::string check_addr;
|
||||||
int pos = addr.find('/');
|
int pos = addr.find('/');
|
||||||
int pos2 = addr.find(':');
|
int pos2 = addr.find(':');
|
||||||
|
|||||||
+77
-21
@@ -117,11 +117,13 @@ void msgr_iothread_t::run()
|
|||||||
|
|
||||||
void osd_messenger_t::init()
|
void osd_messenger_t::init()
|
||||||
{
|
{
|
||||||
|
// FIXME: Support multiple RDMA networks?!
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
if (use_rdma)
|
if (use_rdma)
|
||||||
{
|
{
|
||||||
rdma_context = msgr_rdma_context_t::create(
|
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
|
rdma_port_num, rdma_gid_index, rdma_mtu, rdma_odp, log_level
|
||||||
);
|
);
|
||||||
if (!rdma_context)
|
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)
|
if (!this->rdma_max_msg || this->rdma_max_msg > 128*1024*1024)
|
||||||
this->rdma_max_msg = 129*1024;
|
this->rdma_max_msg = 129*1024;
|
||||||
this->rdma_odp = config["rdma_odp"].bool_value();
|
this->rdma_odp = config["rdma_odp"].bool_value();
|
||||||
std::vector<std::string> 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
|
#endif
|
||||||
if (!osd_num)
|
if (!osd_num)
|
||||||
this->iothread_count = (uint32_t)config["client_iothread_count"].uint64_value();
|
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)
|
if (!this->osd_ping_timeout)
|
||||||
this->osd_ping_timeout = 5;
|
this->osd_ping_timeout = 5;
|
||||||
this->log_level = config["log_level"].uint64_value();
|
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)
|
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){
|
wanted_peers[peer_osd].raw_address_list = peer_state["addresses"];
|
||||||
.address_list = peer_state["addresses"],
|
// We are an OSD -> try to select a cluster address
|
||||||
.port = (int)peer_state["port"].int64_value(),
|
// 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].port = (int)peer_state["port"].int64_value();
|
||||||
{
|
|
||||||
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;
|
|
||||||
try_connect_peer(peer_osd);
|
try_connect_peer(peer_osd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-6
@@ -16,6 +16,7 @@
|
|||||||
#include "json11/json11.hpp"
|
#include "json11/json11.hpp"
|
||||||
#include "msgr_op.h"
|
#include "msgr_op.h"
|
||||||
#include "timerfd_manager.h"
|
#include "timerfd_manager.h"
|
||||||
|
#include "addr_util.h"
|
||||||
#include <ringloop.h>
|
#include <ringloop.h>
|
||||||
|
|
||||||
#define CL_READ_HDR 1
|
#define CL_READ_HDR 1
|
||||||
@@ -93,13 +94,14 @@ struct osd_client_t
|
|||||||
|
|
||||||
struct osd_wanted_peer_t
|
struct osd_wanted_peer_t
|
||||||
{
|
{
|
||||||
|
json11::Json raw_address_list;
|
||||||
json11::Json address_list;
|
json11::Json address_list;
|
||||||
int port;
|
int port = 0;
|
||||||
time_t last_connect_attempt;
|
time_t last_connect_attempt = 0;
|
||||||
bool connecting, address_changed;
|
bool connecting = false, address_changed = false;
|
||||||
int address_index;
|
int address_index = 0;
|
||||||
std::string cur_addr;
|
std::string cur_addr;
|
||||||
int cur_port;
|
int cur_port = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct osd_op_stats_t
|
struct osd_op_stats_t
|
||||||
@@ -165,7 +167,6 @@ protected:
|
|||||||
|
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
bool use_rdma = true;
|
bool use_rdma = true;
|
||||||
std::vector<std::string> osd_networks;
|
|
||||||
std::string rdma_device;
|
std::string rdma_device;
|
||||||
uint64_t rdma_port_num = 1, rdma_mtu = 0;
|
uint64_t rdma_port_num = 1, rdma_mtu = 0;
|
||||||
int rdma_gid_index = -1;
|
int rdma_gid_index = -1;
|
||||||
@@ -190,6 +191,12 @@ public:
|
|||||||
std::map<int, osd_client_t*> clients;
|
std::map<int, osd_client_t*> clients;
|
||||||
std::map<osd_num_t, osd_wanted_peer_t> wanted_peers;
|
std::map<osd_num_t, osd_wanted_peer_t> wanted_peers;
|
||||||
std::map<uint64_t, int> osd_peer_fds;
|
std::map<uint64_t, int> osd_peer_fds;
|
||||||
|
std::vector<std::string> osd_networks;
|
||||||
|
std::vector<addr_mask_t> osd_network_masks;
|
||||||
|
std::vector<std::string> osd_cluster_networks;
|
||||||
|
std::vector<addr_mask_t> osd_cluster_network_masks;
|
||||||
|
std::vector<std::string> all_osd_networks;
|
||||||
|
std::vector<addr_mask_t> all_osd_network_masks;
|
||||||
// op statistics
|
// op statistics
|
||||||
osd_op_stats_t stats, recovery_stats;
|
osd_op_stats_t stats, recovery_stats;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "addr_util.h"
|
|
||||||
#include "msgr_rdma.h"
|
#include "msgr_rdma.h"
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
|
|
||||||
@@ -77,7 +76,7 @@ static bool is_ipv4_gid(ibv_gid_entry *gidx)
|
|||||||
((uint32_t*)gidx->gid.raw)[2] == 0xffff0000);
|
((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 &&
|
if (gidx->gid_type != IBV_GID_TYPE_ROCE_V1 &&
|
||||||
gidx->gid_type != IBV_GID_TYPE_ROCE_V2 ||
|
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;
|
matched_dev best;
|
||||||
ibv_device_attr attr;
|
ibv_device_attr attr;
|
||||||
@@ -201,7 +200,7 @@ cleanup:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
msgr_rdma_context_t *msgr_rdma_context_t::create(std::vector<std::string> 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<addr_mask_t> & osd_network_masks, const char *ib_devname, uint8_t ib_port, int gid_index, uint32_t mtu, bool odp, int log_level)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
ibv_device **dev_list = NULL;
|
ibv_device **dev_list = NULL;
|
||||||
@@ -242,14 +241,9 @@ msgr_rdma_context_t *msgr_rdma_context_t::create(std::vector<std::string> osd_ne
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef IBV_ADVISE_MR_ADVICE_PREFETCH_NO_FAULT
|
#ifdef IBV_ADVISE_MR_ADVICE_PREFETCH_NO_FAULT
|
||||||
else if (osd_networks.size())
|
else if (osd_network_masks.size())
|
||||||
{
|
{
|
||||||
std::vector<addr_mask_t> nets;
|
auto best = match_device(dev_list, osd_network_masks.data(), osd_network_masks.size(), log_level);
|
||||||
for (auto & netstr: osd_networks)
|
|
||||||
{
|
|
||||||
nets.push_back(cidr_parse(netstr));
|
|
||||||
}
|
|
||||||
auto best = match_device(dev_list, nets.data(), nets.size(), log_level);
|
|
||||||
if (best.dev == -2)
|
if (best.dev == -2)
|
||||||
{
|
{
|
||||||
best.dev = 0;
|
best.dev = 0;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <infiniband/verbs.h>
|
#include <infiniband/verbs.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "addr_util.h"
|
||||||
|
|
||||||
struct msgr_rdma_address_t
|
struct msgr_rdma_address_t
|
||||||
{
|
{
|
||||||
@@ -36,7 +37,7 @@ struct msgr_rdma_context_t
|
|||||||
int max_cqe = 0;
|
int max_cqe = 0;
|
||||||
int used_max_cqe = 0;
|
int used_max_cqe = 0;
|
||||||
|
|
||||||
static msgr_rdma_context_t *create(std::vector<std::string> 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<addr_mask_t> & 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();
|
~msgr_rdma_context_t();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -98,5 +98,3 @@ std::string format_lat(uint64_t lat);
|
|||||||
std::string format_q(double depth);
|
std::string format_q(double depth);
|
||||||
|
|
||||||
bool stupid_glob(const std::string str, const std::string glob);
|
bool stupid_glob(const std::string str, const std::string glob);
|
||||||
|
|
||||||
std::string implode(const std::string & sep, json11::Json array);
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "epoll_manager.h"
|
#include "epoll_manager.h"
|
||||||
#include "pg_states.h"
|
#include "pg_states.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
#include "json_util.h"
|
||||||
|
|
||||||
struct placement_osd_t
|
struct placement_osd_t
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "cluster_client.h"
|
#include "cluster_client.h"
|
||||||
#include "pg_states.h"
|
#include "pg_states.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
#include "json_util.h"
|
||||||
|
|
||||||
struct pg_lister_t
|
struct pg_lister_t
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "epoll_manager.h"
|
#include "epoll_manager.h"
|
||||||
#include "pg_states.h"
|
#include "pg_states.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
#include "json_util.h"
|
||||||
|
|
||||||
struct pool_creator_t
|
struct pool_creator_t
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-16
@@ -5,6 +5,7 @@
|
|||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
#include "cluster_client.h"
|
#include "cluster_client.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
#include "json_util.h"
|
||||||
#include "pg_states.h"
|
#include "pg_states.h"
|
||||||
|
|
||||||
// List pools with space statistics
|
// List pools with space statistics
|
||||||
@@ -665,19 +666,3 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_pool_ls(json11::Json cfg)
|
|||||||
return false;
|
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;
|
|
||||||
}
|
|
||||||
|
|||||||
+33
-32
@@ -120,7 +120,9 @@ osd_t::~osd_t()
|
|||||||
delete epmgr;
|
delete epmgr;
|
||||||
if (bs)
|
if (bs)
|
||||||
delete bs;
|
delete bs;
|
||||||
close(listen_fd);
|
for (auto listen_fd: listen_fds)
|
||||||
|
close(listen_fd);
|
||||||
|
listen_fds.clear();
|
||||||
free(zero_buffer);
|
free(zero_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,9 +164,15 @@ void osd_t::parse_config(bool init)
|
|||||||
else
|
else
|
||||||
immediate_commit = IMMEDIATE_NONE;
|
immediate_commit = IMMEDIATE_NONE;
|
||||||
// Bind address
|
// Bind address
|
||||||
bind_address = config["bind_address"].string_value();
|
cfg_bind_addresses.clear();
|
||||||
if (bind_address == "")
|
if (config.find("bind_address") != config.end())
|
||||||
bind_address = "0.0.0.0";
|
{
|
||||||
|
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();
|
bind_port = config["bind_port"].uint64_value();
|
||||||
if (bind_port <= 0 || bind_port > 65535)
|
if (bind_port <= 0 || bind_port > 65535)
|
||||||
bind_port = 0;
|
bind_port = 0;
|
||||||
@@ -322,41 +330,34 @@ void osd_t::parse_config(bool init)
|
|||||||
|
|
||||||
void osd_t::bind_socket()
|
void osd_t::bind_socket()
|
||||||
{
|
{
|
||||||
if (config["osd_network"].is_string() ||
|
if (cfg_bind_addresses.size())
|
||||||
config["osd_network"].is_array())
|
|
||||||
{
|
{
|
||||||
std::vector<std::string> mask;
|
bind_addresses = cfg_bind_addresses;
|
||||||
if (config["osd_network"].is_string())
|
}
|
||||||
mask.push_back(config["osd_network"].string_value());
|
else if (msgr.all_osd_network_masks.size())
|
||||||
else
|
{
|
||||||
for (auto v: config["osd_network"].array_items())
|
bind_addresses = getifaddr_list(msgr.all_osd_network_masks);
|
||||||
mask.push_back(v.string_value());
|
if (!bind_addresses.size())
|
||||||
auto matched_addrs = getifaddr_list(mask);
|
|
||||||
if (matched_addrs.size() > 1)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "More than 1 address matches requested network(s): %s\n", json11::Json(matched_addrs).dump().c_str());
|
auto nets = implode(", ", msgr.all_osd_networks);
|
||||||
force_stop(1);
|
|
||||||
}
|
|
||||||
if (!matched_addrs.size())
|
|
||||||
{
|
|
||||||
std::string nets;
|
|
||||||
for (auto v: mask)
|
|
||||||
nets += (nets == "" ? v : ","+v);
|
|
||||||
fprintf(stderr, "Addresses matching osd_network(s) %s not found\n", nets.c_str());
|
fprintf(stderr, "Addresses matching osd_network(s) %s not found\n", nets.c_str());
|
||||||
force_stop(1);
|
force_stop(1);
|
||||||
}
|
}
|
||||||
bind_address = matched_addrs[0];
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
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()
|
bool osd_t::shutdown()
|
||||||
|
|||||||
+3
-2
@@ -107,7 +107,7 @@ class osd_t
|
|||||||
bool no_recovery = false;
|
bool no_recovery = false;
|
||||||
bool no_scrub = false;
|
bool no_scrub = false;
|
||||||
bool allow_net_split = false;
|
bool allow_net_split = false;
|
||||||
std::string bind_address;
|
std::vector<std::string> cfg_bind_addresses;
|
||||||
int bind_port, listen_backlog = 128;
|
int bind_port, listen_backlog = 128;
|
||||||
// FIXME: Implement client queue depth limit
|
// FIXME: Implement client queue depth limit
|
||||||
int client_queue_depth = 128;
|
int client_queue_depth = 128;
|
||||||
@@ -200,7 +200,8 @@ class osd_t
|
|||||||
epoll_manager_t *epmgr = NULL;
|
epoll_manager_t *epmgr = NULL;
|
||||||
|
|
||||||
int listening_port = 0;
|
int listening_port = 0;
|
||||||
int listen_fd = 0;
|
std::vector<std::string> bind_addresses;
|
||||||
|
std::vector<int> listen_fds;
|
||||||
ring_consumer_t consumer;
|
ring_consumer_t consumer;
|
||||||
|
|
||||||
// op statistics
|
// op statistics
|
||||||
|
|||||||
@@ -165,8 +165,8 @@ json11::Json osd_t::get_osd_state()
|
|||||||
hostname.resize(strnlen(hostname.data(), hostname.size()));
|
hostname.resize(strnlen(hostname.data(), hostname.size()));
|
||||||
json11::Json::object st;
|
json11::Json::object st;
|
||||||
st["state"] = "up";
|
st["state"] = "up";
|
||||||
if (bind_address != "0.0.0.0")
|
if (bind_addresses.size() != 1 || bind_addresses[0] != "0.0.0.0")
|
||||||
st["addresses"] = json11::Json::array { bind_address };
|
st["addresses"] = bind_addresses;
|
||||||
else
|
else
|
||||||
st["addresses"] = getifaddr_list();
|
st["addresses"] = getifaddr_list();
|
||||||
st["host"] = std::string(hostname.data(), hostname.size());
|
st["host"] = std::string(hostname.data(), hostname.size());
|
||||||
|
|||||||
@@ -126,13 +126,11 @@ addr_mask_t cidr_parse(std::string mask)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> getifaddr_list(std::vector<std::string> mask_cfg, bool include_v6)
|
std::vector<std::string> getifaddr_list(const std::vector<addr_mask_t> & masks, bool include_v6)
|
||||||
{
|
{
|
||||||
std::vector<addr_mask_t> masks;
|
for (auto & mask: masks)
|
||||||
for (auto mask: mask_cfg)
|
|
||||||
{
|
{
|
||||||
masks.push_back(cidr_parse(mask));
|
if (mask.family == AF_INET6)
|
||||||
if (masks[masks.size()-1].family == AF_INET6)
|
|
||||||
{
|
{
|
||||||
// Auto-enable IPv6 addresses
|
// Auto-enable IPv6 addresses
|
||||||
include_v6 = true;
|
include_v6 = true;
|
||||||
|
|||||||
@@ -18,5 +18,5 @@ std::string addr_to_string(const sockaddr_storage &addr);
|
|||||||
addr_mask_t cidr_parse(std::string mask);
|
addr_mask_t cidr_parse(std::string mask);
|
||||||
bool cidr_match(const in_addr &address, const in_addr &network, uint8_t bits);
|
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);
|
bool cidr6_match(const in6_addr &address, const in6_addr &network, uint8_t bits);
|
||||||
std::vector<std::string> getifaddr_list(std::vector<std::string> mask_cfg = std::vector<std::string>(), bool include_v6 = false);
|
std::vector<std::string> getifaddr_list(const std::vector<addr_mask_t> & masks = std::vector<addr_mask_t>(), bool include_v6 = false);
|
||||||
int create_and_bind_socket(std::string bind_address, int bind_port, int listen_backlog, int *listening_port);
|
int create_and_bind_socket(std::string bind_address, int bind_port, int listen_backlog, int *listening_port);
|
||||||
|
|||||||
@@ -33,3 +33,19 @@ bool json_is_false(const json11::Json & val)
|
|||||||
return !val.bool_value();
|
return !val.bool_value();
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,3 +11,4 @@
|
|||||||
std::map<std::string, std::string> json_to_string_map(const json11::Json::object & config);
|
std::map<std::string, std::string> json_to_string_map(const json11::Json::object & config);
|
||||||
bool json_is_true(const json11::Json & val);
|
bool json_is_true(const json11::Json & val);
|
||||||
bool json_is_false(const json11::Json & val);
|
bool json_is_false(const json11::Json & val);
|
||||||
|
std::string implode(const std::string & sep, json11::Json array);
|
||||||
|
|||||||
Reference in New Issue
Block a user