blob: 8d26cf11bc691c9c74eee0fe05e89fe8a43c9402 [file] [edit]
/*
drbdadm_dump.c
This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
Copyright (C) 2003-2008, LINBIT Information Technologies GmbH.
Copyright (C) 2003-2008, Philipp Reisner <philipp.reisner@linbit.com>.
Copyright (C) 2003-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
drbd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
drbd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with drbd; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "drbdadm.h"
#include "drbdtool_common.h"
static int indent = 0;
#define INDENT_WIDTH 4
#define BFMT "%s;\n"
#define IPV4FMT "%-16s %s %s:%s%s"
#define IPV6FMT "%-16s %s [%s]:%s%s"
#define MDISK "%-16s %s;\n"
#define MDISKI "%-16s %s [%s];\n"
#define printI(fmt, args... ) printf("%*s" fmt,INDENT_WIDTH * indent,"" , ## args )
#define printA(name, val ) \
printf("%*s%*s %3s;\n", \
INDENT_WIDTH * indent,"" , \
-24+INDENT_WIDTH * indent, \
name, val )
static void dump_options(char *name, struct options *options);
static void __dump_options(struct options *options)
{
struct d_option *option;
STAILQ_FOREACH(option, options, link) {
if (option->value)
printA(option->name,
option->is_escaped ? option->value : esc(option->
value));
else
printI(BFMT, option->name);
}
}
static void dump_options2(char *name, struct options *options,
void(*within)(struct options *), struct options *ctx)
{
if (STAILQ_EMPTY(options) && (!ctx || (ctx && STAILQ_EMPTY(ctx))))
return;
printI("%s {\n", name);
++indent;
__dump_options(options);
if (within)
within(ctx);
--indent;
printI("}\n");
}
static void dump_peer_device_options(struct options *options)
{
if (!STAILQ_EMPTY(options)) {
/* printI("# peer device options:\n"); */
__dump_options(options);
}
}
static void dump_options(char *name, struct options *options)
{
dump_options2(name, options, NULL, NULL);
}
static void dump_proxy_plugins(struct options *options)
{
dump_options("plugin", options);
}
void dump_global_info()
{
static const char * const yes_no_ask[] = {
[UC_YES] = "yes",
[UC_NO] = "no",
[UC_ASK] = "ask",
};
if (!global_options.minor_count
&& !global_options.disable_ip_verification
&& global_options.dialog_refresh == 1
&& global_options.usage_count == UC_ASK
&& !verbose)
return;
printI("global {\n");
++indent;
if (global_options.disable_ip_verification)
printI("disable-ip-verification;\n");
if (global_options.minor_count)
printI("minor-count %i;\n", global_options.minor_count);
if (global_options.dialog_refresh != 1)
printI("dialog-refresh %i;\n", global_options.dialog_refresh);
if (global_options.usage_count != UC_ASK)
printI("usage-count %s;\n", yes_no_ask[global_options.usage_count]);
--indent;
printI("}\n\n");
}
static void fake_startup_options(struct d_resource *res);
static void dump_common_info()
{
if (!common)
return;
printI("common {\n");
++indent;
fake_startup_options(common);
dump_options("options", &common->res_options);
dump_options("net", &common->net_options);
dump_options2("disk", &common->disk_options,
dump_peer_device_options, &common->pd_options);
dump_options("startup", &common->startup_options);
dump_options2("proxy", &common->proxy_options,
dump_proxy_plugins, &common->proxy_plugins);
dump_options("handlers", &common->handlers);
--indent;
printf("}\n\n");
}
static void dump_address(char *name, struct d_address *address, char *postfix)
{
if (!strcmp(address->af, "ipv6"))
printI(IPV6FMT, name, address->af, address->addr, address->port, postfix);
else
printI(IPV4FMT, name, address->af, address->addr, address->port, postfix);
}
static void dump_proxy_info(const char *prefix, struct d_proxy_info *pi)
{
printI("%sproxy on %s {\n", prefix, names_to_str(&pi->on_hosts));
++indent;
dump_address("inside", &pi->inside, ";\n");
dump_address("outside", &pi->outside, ";\n");
dump_options2("options", &pi->options, dump_proxy_plugins, &pi->plugins);
--indent;
printI("}\n");
}
static void dump_volume(int has_lower, struct d_volume *vol)
{
if (!vol->implicit) {
printI("volume %d {\n", vol->vnr);
++indent;
}
/* Handle volume of '_remote_host' */
if (!vol->device && !vol->disk && !vol->meta_disk && !vol->meta_index)
goto out;
dump_options2("disk", &vol->disk_options,
dump_peer_device_options, &vol->pd_options);
if (vol->parsed_device || verbose) {
printI("device%*s", -19 + INDENT_WIDTH * indent, "");
if (vol->device)
printf("%s ", esc(vol->device));
printf("minor %d;\n", vol->device_minor);
}
if (!has_lower && (vol->parsed_disk || verbose))
printA("disk", esc(vol->disk ? vol->disk : "none"));
if (!has_lower && (vol->parsed_meta_disk || verbose) && vol->disk) {
if (!strcmp(vol->meta_index, "flexible"))
printI(MDISK, "meta-disk", esc(vol->meta_disk));
else if (!strcmp(vol->meta_index, "internal"))
printA("meta-disk", "internal");
else
printI(MDISKI, "meta-disk", esc(vol->meta_disk),
vol->meta_index);
}
if (!vol->implicit) {
out:
--indent;
printI("}\n");
}
}
static void dump_host_info(struct d_host_info *hi)
{
struct d_volume *vol;
if (!hi) {
printI(" # No host section data available.\n");
return;
}
if (hi->implicit && !verbose)
return;
if (hi->lower) {
printI("stacked-on-top-of %s {\n", esc(hi->lower->name));
++indent;
printI("# on %s \n", names_to_str(&hi->on_hosts));
} else if (hi->by_address) {
dump_address("floating", &hi->address, " {\n");
++indent;
} else {
printI("on %s {\n", names_to_str(&hi->on_hosts));
++indent;
}
printI("node-id %s;\n", hi->node_id);
dump_options("options", &hi->res_options);
for_each_volume(vol, &hi->volumes) {
if (vol->parsed_device || vol->parsed_disk || vol->parsed_meta_disk || verbose)
dump_volume(!!hi->lower, vol);
}
if (!hi->by_address && hi->address.addr)
dump_address("address", &hi->address, ";\n");
if (hi->proxy_compat_only && !verbose)
dump_proxy_info("", hi->proxy_compat_only);
--indent;
printI("}\n");
}
static void dump_hname_address_pairs(struct hname_address_pairs *hname_address_pairs)
{
struct hname_address *ha;
STAILQ_FOREACH(ha, hname_address_pairs, link) {
if (ha->by_address || ha->faked_hostname) {
dump_address("address", &ha->address,
ssprintf("%s# on %s\n", ha->proxy ? " " : "; ", ha->name));
} else {
printI("host %s", ha->name);
if (ha->parsed_address || (verbose && ha->address.addr))
dump_address(" address", &ha->address, "");
else if (ha->parsed_port)
printf(" port %s", ha->address.port);
}
if (ha->proxy)
dump_proxy_info(" via ", ha->proxy);
else
printf(";\n");
}
}
static void dump_connection(struct connection *conn)
{
struct peer_device *pd;
struct path *path;
if (conn->implicit && !verbose)
return;
printI("connection");
if (conn->name)
printf(" %s", esc(conn->name));
printf(" {\n");
++indent;
path = STAILQ_FIRST(&conn->paths);
if (path->implicit && !verbose) {
dump_hname_address_pairs(&path->hname_address_pairs);
} else {
for_each_path(path, &conn->paths) {
printI("path {\n");
++indent;
dump_hname_address_pairs(&path->hname_address_pairs);
--indent;
printI("}\n");
}
}
dump_options("net", &conn->net_options);
dump_options("disk", &conn->pd_options);
STAILQ_FOREACH(pd, &conn->peer_devices, connection_link) {
if (pd->implicit && !verbose)
continue;
printI("volume %d {\n", pd->vnr);
++indent;
dump_options("disk", &pd->pd_options);
--indent;
printI("}\n");
}
--indent;
printI("}\n");
}
static void __dump_options_xml(struct options *options)
{
struct d_option *option;
STAILQ_FOREACH(option, options, link) {
if (option->value)
printI("<option name=\"%s\" value=\"%s\"/>\n",
option->name,
option->is_escaped ? option->value : esc_xml(option->
value));
else
printI("<option name=\"%s\"/>\n", option->name);
}
}
static void dump_options_xml2(char *name, struct options *options,
void(*within)(struct options *), struct options *ctx)
{
if (STAILQ_EMPTY(options) && (!ctx || (ctx && STAILQ_EMPTY(ctx))))
return;
printI("<section name=\"%s\">\n", name);
++indent;
__dump_options_xml(options);
if (within)
within(ctx);
--indent;
printI("</section>\n");
}
static void dump_peer_device_options_xml(struct options *options)
{
if (!STAILQ_EMPTY(options))
__dump_options_xml(options);
}
static void dump_options_xml(char *name, struct options *options)
{
dump_options_xml2(name, options, NULL, NULL);
}
static void dump_proxy_plugins_xml(struct options *options)
{
dump_options_xml("plugin", options);
}
static void dump_global_info_xml()
{
if (!global_options.minor_count
&& !global_options.disable_ip_verification
&& global_options.dialog_refresh == 1)
return;
printI("<global>\n");
++indent;
if (global_options.disable_ip_verification)
printI("<disable-ip-verification/>\n");
if (global_options.minor_count)
printI("<minor-count count=\"%i\"/>\n",
global_options.minor_count);
if (global_options.dialog_refresh != 1)
printI("<dialog-refresh refresh=\"%i\"/>\n",
global_options.dialog_refresh);
--indent;
printI("</global>\n");
}
static void dump_common_info_xml()
{
if (!common)
return;
printI("<common>\n");
++indent;
fake_startup_options(common);
dump_options_xml("options", &common->res_options);
dump_options_xml("net", &common->net_options);
dump_options_xml2("disk", &common->disk_options,
dump_peer_device_options_xml, &common->pd_options);
dump_options_xml("startup", &common->startup_options);
dump_options_xml2("proxy", &common->proxy_options,
dump_proxy_plugins, &common->proxy_plugins);
dump_options_xml("handlers", &common->handlers);
--indent;
printI("</common>\n");
}
static void dump_proxy_info_xml(struct d_proxy_info *pi)
{
printI("<proxy hostname=\"%s\">\n", names_to_str(&pi->on_hosts));
++indent;
printI("<inside family=\"%s\" port=\"%s\">%s</inside>\n", pi->inside.af,
pi->inside.port, pi->inside.addr);
printI("<outside family=\"%s\" port=\"%s\">%s</outside>\n",
pi->outside.af, pi->outside.port, pi->outside.addr);
dump_options_xml2("options", &pi->options, dump_proxy_plugins_xml, &pi->plugins);
--indent;
printI("</proxy>\n");
}
static void dump_volume_xml(struct d_volume *vol)
{
printI("<volume vnr=\"%d\">\n", vol->vnr);
++indent;
dump_options_xml2("disk", &vol->disk_options,
dump_peer_device_options_xml, &vol->pd_options);
printI("<device minor=\"%d\">%s</device>\n", vol->device_minor,
esc_xml(vol->device));
printI("<disk>%s</disk>\n", esc_xml(vol->disk));
if (vol->meta_index) {
/* Stacked do not have this... */
if (!strcmp(vol->meta_index, "flexible"))
printI("<meta-disk>%s</meta-disk>\n",
esc_xml(vol->meta_disk));
else if (!strcmp(vol->meta_index, "internal"))
printI("<meta-disk>internal</meta-disk>\n");
else {
printI("<meta-disk index=\"%s\">%s</meta-disk>\n",
vol->meta_index, esc_xml(vol->meta_disk));
}
}
--indent;
printI("</volume>\n");
}
static void dump_host_info_xml(struct d_host_info *hi)
{
struct d_volume *vol;
if (!hi) {
printI("<!-- No host section data available. -->\n");
return;
}
if (hi->by_address)
printI("<host floating=\"1\">\n");
else
printI("<host name=\"%s\">\n", names_to_str(&hi->on_hosts));
++indent;
dump_options_xml("options", &hi->res_options);
for_each_volume(vol, &hi->volumes)
dump_volume_xml(vol);
printI("<address family=\"%s\" port=\"%s\">%s</address>\n",
hi->address.af, hi->address.port, hi->address.addr);
--indent;
printI("</host>\n");
}
static void dump_hname_address_pairs_xml(struct hname_address_pairs *hname_address_pairs)
{
struct hname_address *ha;
STAILQ_FOREACH(ha, hname_address_pairs, link) {
printI("<host name=\"%s\">", ha->name);
if (ha->proxy) {
printf("\n");
++indent;
printI("");
}
if (ha->address.addr)
printf("<address family=\"%s\" port=\"%s\">%s</address>",
ha->address.af, ha->address.port, ha->address.addr);
else
printf("<address family=\"%s\" port=\"%s\">%s</address>",
ha->host_info->address.af, ha->host_info->address.port,
ha->host_info->address.addr);
if (ha->proxy) {
printf("\n");
dump_proxy_info_xml(ha->proxy);
--indent;
printI("</host>\n");
} else {
printf("</host>\n");
}
}
}
static void dump_connection_xml(struct connection *conn)
{
struct peer_device *pd;
struct path *path;
if (conn->name)
printI("<connection name=\"%s\">\n", esc_xml(conn->name));
else
printI("<connection>\n");
++indent;
path = STAILQ_FIRST(&conn->paths);
if (path->implicit && !verbose) {
dump_hname_address_pairs_xml(&path->hname_address_pairs);
} else {
for_each_path(path, &conn->paths) {
printI("<path>\n");
++indent;
dump_hname_address_pairs_xml(&path->hname_address_pairs);
--indent;
printI("</path>\n");
}
}
dump_options_xml("net", &conn->net_options);
dump_options_xml("disk", &conn->pd_options);
STAILQ_FOREACH(pd, &conn->peer_devices, connection_link) {
if (pd->implicit && !verbose)
continue;
printI("<volume vnr=\"%d\">\n", pd->vnr);
++indent;
dump_options_xml("disk", &pd->pd_options);
--indent;
printI("</volume>\n");
}
--indent;
printI("</connection>\n");
}
static void fake_startup_options(struct d_resource *res)
{
struct d_option *opt;
if (res->stacked_timeouts) {
opt = new_opt(strdup("stacked-timeouts"), NULL);
insert_tail(&res->startup_options, opt);
}
}
static void dump_mesh(struct d_resource *res)
{
struct mesh *mesh;
STAILQ_FOREACH(mesh, &res->meshes, link) {
struct d_name *h;
printI("connection-mesh {\n");
++indent;
printI("hosts");
STAILQ_FOREACH(h, &mesh->hosts, link)
printf(" %s", h->name);
printf(";\n");
dump_options("net", &mesh->net_options);
--indent;
printI("}\n");
}
}
int adm_dump(const struct cfg_ctx *ctx)
{
struct d_host_info *host;
struct d_resource *res = ctx->res;
struct connection *conn;
struct d_volume *vol;
if (!res) {
printI("# no resources configured\n");
return 0;
}
printI("# resource %s on %s: %s, %s\n",
esc(res->name), hostname,
res->ignore ? "ignored" : "not ignored",
res->stacked ? "stacked" : "not stacked");
printI("# defined at %s:%u\n", res->config_file, res->start_line);
printI("resource %s {\n", esc(res->name));
++indent;
if (res->template)
printI("template-file %s;\n", esc(res->template->config_file));
for_each_volume(vol, &res->volumes) {
if (!verbose && (vol->parsed_device || vol->parsed_disk || vol->parsed_meta_disk))
dump_volume(res->stacked, vol);
}
for_each_host(host, &res->all_hosts)
dump_host_info(host);
for_each_connection(conn, &res->connections)
dump_connection(conn);
if (!verbose)
dump_mesh(res);
fake_startup_options(res);
dump_options("options", &res->res_options);
dump_options("net", &res->net_options);
dump_options2("disk", &res->disk_options,
dump_peer_device_options, &res->pd_options);
dump_options("startup", &res->startup_options);
dump_options2("proxy", &res->proxy_options,
dump_proxy_plugins, &res->proxy_plugins);
dump_options("handlers", &res->handlers);
--indent;
printf("}\n\n");
return 0;
}
int adm_dump_xml(const struct cfg_ctx *ctx)
{
struct d_host_info *host;
struct d_resource *res = ctx->res;
struct connection *conn;
if (!res) {
printI("<!-- No resources configured -->\n");
return 0;
}
printI("<resource name=\"%s\" conf-file-line=\"%s:%u\">\n",
esc_xml(res->name),
esc_xml(res->config_file), res->start_line);
++indent;
if (res->template)
printI("<template file=\"%s\">\n", esc_xml(res->template->config_file));
// else if (common && common->protocol) printA("# common protocol", common->protocol);
for_each_host(host, &res->all_hosts)
dump_host_info_xml(host);
for_each_connection(conn, &res->connections)
dump_connection_xml(conn);
fake_startup_options(res);
dump_options_xml("options", &res->res_options);
dump_options_xml("net", &res->net_options);
dump_options_xml2("disk", &res->disk_options,
dump_peer_device_options_xml, &res->pd_options);
dump_options_xml("startup", &res->startup_options);
dump_options_xml2("proxy", &res->proxy_options,
dump_proxy_plugins_xml, &res->proxy_plugins);
dump_options_xml("handlers", &res->handlers);
--indent;
printI("</resource>\n");
return 0;
}
void print_dump_xml_header(void)
{
printf("<config file=\"%s\">\n", config_save);
++indent;
dump_global_info_xml();
dump_common_info_xml();
}
void print_dump_header(void)
{
printf("# %s\n", config_save);
dump_global_info();
dump_common_info();
}