From 1f9683be124efefa40c12ef24befbe496cfd11a7 Mon Sep 17 00:00:00 2001 From: CV/BH/SC Date: Tue, 9 Sep 2014 11:35:33 -0500 Subject: [PATCH 01/37] Reworked riak_repl.schema --- priv/riak_repl.schema | 211 ++++++++++++++++++++++++------------------ 1 file changed, 121 insertions(+), 90 deletions(-) diff --git a/priv/riak_repl.schema b/priv/riak_repl.schema index b76a93de..649044f1 100644 --- a/priv/riak_repl.schema +++ b/priv/riak_repl.schema @@ -2,9 +2,10 @@ %% Replication config %% @doc Path (relative or absolute) to the working directory for the -%% replication process +%% replication process. {mapping, "mdc.data_root", "riak_repl.data_root", [ - {default, "{{repl_data_root}}"} + {default, "{{repl_data_root}}"}, + {datatype, directory} ]}. %% @doc The cluster manager will listen for connections from remote @@ -12,134 +13,164 @@ %% but only the cluster manager running on the cluster_leader will %% service requests. This can change as nodes enter and leave the %% cluster. The value is a combination of an IP address (**not -%% hostname**) followed by a port number +%% hostname**) followed by a port number. {mapping, "mdc.cluster_manager", "riak_core.cluster_mgr", [ {default, {"{{cluster_manager_ip}}", {{cluster_manager_port}} }}, {datatype, ip} ]}. -%% @doc The hard limit of fullsync workers that will be running on the -%% source side of a cluster across all nodes on that cluster for a -%% fullsync to a sink cluster. This means if one has configured -%% fullsync for two different clusters, both with a -%% max_fssource_cluster of 5, 10 fullsync workers can be in -%% progress. Only affects nodes on the source cluster on which this -%% parameter is defined via the configuration file or command line -{mapping, "mdc.max_fssource_cluster", "riak_repl.max_fssource_cluster", [ +%% @doc The limit of fullsync workers running per source-side of a +%% replication connection. For example, if you have a cluster that is +%% replicating to 3 sink clusters and this set to 5, you will have at +%% most 15 workers in total. +{mapping, "mdc.fullsync.source.max_workers_per_cluster", "riak_repl.max_fssource_cluster", [ {datatype, integer}, {default, 5} ]}. -%% @doc Limits the number of fullsync workers that will be running on -%% each individual node in a source cluster. This is a hard limit for -%% all fullsyncs enabled; additional fullsync configurations will not -%% increase the number of fullsync workers allowed to run on any node. -%% Only affects nodes on the source cluster on which this parameter is -%% defined via the configuration file or command line -{mapping, "mdc.max_fssource_node", "riak_repl.max_fssource_node", [ +%% @doc The limit of fullsync workers running per node in the source +%% cluster. This setting is independent of the number of replication +%% connections. Thus, multiple simultaneous sink connections from the +%% source cluster will have to share the source node's number of +%% maximum connections. For example, if you have a cluster that is +%% replicating to 3 sink cluster and this is set to 1, you will have +%% at most 1 worker per node. +{mapping, "mdc.fullsync.source.max_workers_per_node", "riak_repl.max_fssource_node", [ {datatype, integer}, {default, 1} ]}. -%% @doc Limits the number of fullsync workers allowed to run on each -%% individual node in a sink cluster. This is a hard limit for all -%% fullsync sources interacting with the sink cluster. Thus, multiple -%% simultaneous source connections to the sink cluster will have to -%% share the sink node's number of maximum connections. Only affects -%% nodes on the sink cluster on which this parameter is defined via -%% the configuration file or command line. -{mapping, "mdc.max_fssink_node", "riak_repl.max_fssink_node", [ +%% @doc The limit of fullsync workers running per node in the sink +%% cluster. This setting is independent of the number of replication +%% connections. Thus, multiple simultaneous source connections to the +%% sink cluster will have to share the sink node's number of maximum +%% connections. For example, if you have a cluster that is replicating +%% from 3 source clusters and this is set to 1, you will have at most +%% 1 worker per node. +{mapping, "mdc.fullsync.sink.max_workers_per_node", "riak_repl.max_fssink_node", [ {datatype, integer}, {default, 1} ]}. -%% @doc Whether to initiate a fullsync on initial connection from the -%% secondary cluster -{mapping, "mdc.fullsync_on_connect", "riak_repl.fullsync_on_connect", [ - {datatype, {enum, [true, false]}}, - {default, true} +%% @doc Whether to initiate a fullsync on initial connection from a +%% sink cluster. +{mapping, "mdc.fullsync.start_on_connect", "riak_repl.fullsync_on_connect", [ + {datatype, flag}, + {default, on} ]}. -%% @doc a single integer value representing the duration to wait in -%% minutes between fullsyncs, or a list of {clustername, -%% time_in_minutes} pairs for each sink participating in fullsync -%% replication. -{mapping, "mdc.fullsync_interval.$cluster_name", "riak_repl.fullsync_interval", [ - {datatype, {duration, ms}}, - {include_default, "all"}, - {commented, "30m"} +%% @doc The duration to wait between initiating fullsyncs for all +%% connected sink clusters. If set to "never", fullsync will not be +%% automatically initiated. If set to "per_sink", individual intervals +%% should be set using mdc.fullsync.interval. +%% @see mdc.fullsync.interval.$cluster_name +{mapping, "mdc.fullsync.interval", "riak_repl.fullsync_interval", [ + {datatype, [{duration, m}, {atom, per_sink}, {atom, never}]}, + {default, "6h"} +]}. + +%% @doc The duration to wait between initiating fullsync with a +%% specific connected sink cluster. +%% @see mdc.fullsync.interval +{mapping, "mdc.fullsync.interval.$cluster_name", "riak_repl.fullsync_interval", [ + {datatype, {duration, m}}, + {include_default, "sink_cluster"}, + {commented, "2d"} ]}. {translation, "riak_repl.fullsync_interval", fun(Conf) -> - Minute = fun(Millis) -> Millis div 60000 end, - FullSyncIntervals = cuttlefish_variable:filter_by_prefix("mdc.fullsync_interval", Conf), - case proplists:get_value(["mdc", "fullsync_interval", "all"], FullSyncIntervals) of - undefined -> - [ {list_to_atom(Name), Minute(Value)} || {["mdc", "fullsync_interval", Name], Value} <- FullSyncIntervals]; - X -> Minute(X) - end + FullSyncIntervals = cuttlefish_variable:filter_by_prefix("mdc.fullsync.interval", Conf), + Partitioned = lists:partition(fun({I, _}) -> ["mdc", "fullsync", "interval"] == I end, FullSyncIntervals), + case Partitioned of + {[{_, never}], Sinks} -> + if length(Sinks) > 0 -> + cuttlefish:warn("mdc.fullsync.interval is set to never," + " sink-specific intervals are ignored"); + true -> ok + end, + disabled; + {[{_, Minutes}], []} when is_integer(Minutes) -> + Minutes; + {[{_, per_sink}], []} -> + cuttlefish:invalid("Cannot set mdc.fullsync.interval = per_sink and" + " omit sink-specific intervals, set sink-specific" + " intervals or use 'never'"); + {[{_, per_sink}], Sinks} -> + [{list_to_atom(SinkName), Value} || {["mdc", "fullsync", "interval", SinkName], Value} <- Sinks ]; + _ -> + cuttlefish:invalid("Cannot set both mdc.fullsync.interval and" + " sink-specific intervals") + end end}. +%% @doc By default, fullsync replication will try to coordinate with +%% other Riak subsystems that may be contending for the same +%% resources. This will help to prevent system response degradation +%% under times of heavy load from multiple background tasks. To +%% disable background coordination, set this parameter to off. +{mapping, "mdc.fullsync.background_manager", "riak_repl.fullsync_use_background_manager", [ + {datatype, flag}, + {default, off}, + hidden +]}. + +%% @doc How frequently the metrics for fullsync source processes should +%% be gathered. The output of `riak-repl status` is calculated on this +%% interval. +{mapping, "mdc.fullsync.source.metrics_refresh_interval", "riak_repl.fullsync_stat_refresh_interval", [ + {datatype, {duration, ms}}, + {commented, "1m"}, + hidden +]}. + %% @doc The maximum size the realtime replication queue can grow to -%% before new objects are dropped. Defaults to 100MB. Dropped objects -%% will need to be replication with a fullsync. -{mapping, "mdc.rtq_max_bytes", "riak_repl.rtq_max_bytes", [ +%% before new objects are dropped. +{mapping, "mdc.realtime.queue_max_bytes", "riak_repl.rtq_max_bytes", [ {datatype, bytesize}, {default, "100MB"} ]}. -%% @doc Enable Riak CS proxy_get and block filter. -{mapping, "mdc.proxy_get", "riak_repl.proxy_get", [ - {datatype, {enum, [on, off]}}, - {default, off} +%% @doc Whether heartbeats are enabled for realtime replication +%% connections. +{mapping, "mdc.realtime.heartbeat", "riak_repl.rt_heartbeat_interval", [ + {datatype, flag} + {default, on} ]}. -{translation, - "riak_repl.proxy_get", - fun(Conf) -> - case cuttlefish:conf_get("mdc.proxy_get", Conf) of - on -> enabled; - off -> disabled; - _ -> disabled - end - end}. - -%% @doc A heartbeat message is sent from the source to the sink every -%% heartbeat_interval. Setting heartbeat_interval to undefined -%% disables the realtime heartbeat. This feature is only available in -%% Riak Enterprise 1.3.2+. -{mapping, "mdc.realtime.heartbeat_interval", "riak_repl.rt_heartbeat_interval", [ +%% @doc When heartbeats are enabled, this setting is the interval +%% between heartbeat messages over the realtime replication +%% connection. +%% @see mdc.realtime.heartbeat +{mapping, "mdc.realtime.heartbeat.interval", "riak_repl.rt_heartbeat_interval", [ {datatype, {duration, s}}, {default, "15s"} ]}. -%% @doc If a heartbeat response is not received in -%% rt_heartbeat_timeout seconds, then the source connection exits and -%% will be re-established. This feature is only available in Riak -%% Enterprise 1.3.2+. -{mapping, "mdc.realtime.heartbeat_timeout", "riak_repl.rt_heartbeat_timeout", [ +%% @doc When heartbeats are enabled, this setting is the amount of +%% time to wait for a heartbeat response from the sink. If a heartbeat +%% response is not received within this time, then the source +%% connection closes and will be re-established. +%% @see mdc.realtime.heartbeat +{mapping, "mdc.realtime.heartbeat.timeout", "riak_repl.rt_heartbeat_timeout", [ {datatype, {duration, s}}, {default, "15s"} ]}. -%% @doc By default, fullsync replication will try to coordinate with other -%% riak subsystems that may be contending for the same resources. This will help -%% to prevent system response degradation under times of heavy load from multiple -%% background tasks. To disable background coordination, set this parameter to false. -%% Enterprise 2.0+. -{mapping, "mdc.fullsync.use_bg_manager", "riak_repl.fullsync_use_background_manager", [ - {datatype, {enum, [true, false]}}, - {level, advanced}, - {default, false} -]}. +{translation, "riak_repl.rt_heartbeat_interval", + fun(Conf) -> + case cuttlefish:conf_get("mdc.realtime.heartbeat", Conf) of + true -> + cuttlefish:conf_get("mdc.realtime.hearbeat.interval", Conf); + false -> + undefined + end + end}. -%% @doc How frequently the stats for fullsync source processes should be -%% gathered. Requests for fullsync status always returned the most recently -%% gathered data, and thus can be at most as old as this value. -{mapping, "mdc.fullsync.stat_refresh_interval", "riak_repl.fullsync_stat_refresh_interval", [ - {datatype, {duration, ms}}, - {commented, "1m"} +%% @doc Enable Riak CS proxy_get and block filter. +{mapping, "mdc.proxy_get", "riak_repl.proxy_get", [ + {datatype, {enum, [{on, enabled}, {off, disabled}]}}, + {default, off}, + hidden ]}. - From fb7de5605a6f99bf3d9f589fb306292fe072462d Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Wed, 10 Sep 2014 16:59:42 -0500 Subject: [PATCH 02/37] WIP sample outputs for riak-repl script --- priv/commands.txt | 129 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 priv/commands.txt diff --git a/priv/commands.txt b/priv/commands.txt new file mode 100644 index 00000000..1a3f1a6d --- /dev/null +++ b/priv/commands.txt @@ -0,0 +1,129 @@ +Usage: riak-repl [ ...] [ ...] + + Commands: + modes Show or set replication modes + status Display status and metrics + + Version 3 Commands: + clustername Show or set the cluster name + clusterstats Display cluster stats + connect Connect to a remote cluster + connections Display a list of connections + disconnect Disconnect from a remote cluster + fullsync Manipulate fullsync replication + nat-map Manipulate NAT mappings + proxy-get Manipulate proxy-get + realtime Manipulate realtime replication + + Version 2 Commands: + add-listener Add a sink listener + add-nat-listener Add a sink listener with NAT + add-site Add a sink site + cancel-fullsync Cancel running fullsync replication + del-listener Delete a sink listener + del-site Delete a sink site + pause-fullsync Pause running fullsync replication + resume-fullsync Resume paused fullsync replication + start-fullsync Start fullsync replication + +Usage: riak-repl realtime [ ...] + + Manipulate realtime replication. Realtime replication streams + incoming writes on the source cluster to the sink cluster(s). + + Sub-commands: + enable Enable realtime replication + disable Disable realtime replication + start Start realtime replication + stop Stop realtime replication + cascades Manipulate cascading realtime replication + +Usage: riak-repl realtime cascades + + Manipulate cascading realtime replication. When this cluster is a + sink and is receiving realtime replication, it can propagate + incoming writes to any clusters for which it is a source and + realtime replication is enabled. + + Sub-commands: + enable Enable cascading realtime replication + disable Disable cascading realtime replication + show Show the current cascading realtime replication setting + +Usage: riak-repl fullsync [ ...] + + Manipulate fullsync replication. Fullsync replication compares data + on the source and the sink and then sends detected differences to + the sink cluster. + + Sub-commands: + enable Enable fullsync replication + disable Disable fullsync replication + start Start fullsync replication + stop Stop fullsync replication + source Manipulate source cluster limits + sink Manipulate sink cluster limits + +Usage: riak-repl fullsync source (max_workers_per_node | max_workers_per_cluster) [] + + Set limits on the number of fullsync workers on a source cluster. If + is omitted, the current setting is displayed. + +Usage: riak-repl fullsync sink max_workers_per_node [] + + Set limits on the number of fullsync workers on a sink cluster. If + is omitted, the current setting is displayed. + +Usage: riak-repl nat-map [ ...] + + Manipulate NAT mappings. NAT mappings allow replication connections + to traverse firewalls between private networks on previously + configured ports. + + Sub-commands: + add Add a NAT mapping + del Delete a NAT mapping + show Display the NAT mapping table + +Usage: riak-repl nat-map add [:] + + Add a NAT mapping from the given external IP to the given internal + IP. An optional external port can be supplied. + +Usage: riak-repl nat-map del [:] + + Delete a NAT mapping between the given external IP and the given + internal IP. + +Usage: riak-repl proxy-get [ ... ] + + Manipulate proxy-get functionality. Proxy-get allows sink clusters + to actively fetch remote objects over a realtime replication + connection. Currently, this is only used by Riak CS. + + Sub-commands: + enable Enable proxy-get on the source + disable Disable proxy-get on the source + redirect Manipulation proxy-get redirection + +Usage: riak-repl proxy-get enable + + Enables proxy-get requests from to this source + cluster. + +Usage: riak-repl proxy-get disable + + Disables proxy-get requests from to this source + cluster. + +Usage: riak-repl proxy-get redirect [ ... ] + + Manipulate proxy-get redirection functionality. Redirection allows + existing proxy-get connections to be redirected to new source + clusters so that the original source cluster can be decommissioned. + + Sub-commands: + add Add a proxy-get redirection + delete Delete an existing proxy-get redirection + show Show a proxy-get redirection + cluster-id Display the local cluster's identifier From cbc96a3be91cacb982d0f006fd75d8a3b738d415 Mon Sep 17 00:00:00 2001 From: CV/SC Date: Mon, 15 Sep 2014 10:32:38 -0500 Subject: [PATCH 03/37] Fix schema errors and tests for new settings --- priv/riak_repl.schema | 6 +++--- test/riak_repl_schema_tests.erl | 21 +++++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/priv/riak_repl.schema b/priv/riak_repl.schema index 649044f1..023fd2a1 100644 --- a/priv/riak_repl.schema +++ b/priv/riak_repl.schema @@ -135,7 +135,7 @@ %% @doc Whether heartbeats are enabled for realtime replication %% connections. {mapping, "mdc.realtime.heartbeat", "riak_repl.rt_heartbeat_interval", [ - {datatype, flag} + {datatype, flag}, {default, on} ]}. @@ -162,7 +162,7 @@ fun(Conf) -> case cuttlefish:conf_get("mdc.realtime.heartbeat", Conf) of true -> - cuttlefish:conf_get("mdc.realtime.hearbeat.interval", Conf); + cuttlefish:conf_get("mdc.realtime.heartbeat.interval", Conf); false -> undefined end @@ -170,7 +170,7 @@ %% @doc Enable Riak CS proxy_get and block filter. {mapping, "mdc.proxy_get", "riak_repl.proxy_get", [ - {datatype, {enum, [{on, enabled}, {off, disabled}]}}, + {datatype, {flag, {on, enabled}, {off, disabled}}}, {default, off}, hidden ]}. diff --git a/test/riak_repl_schema_tests.erl b/test/riak_repl_schema_tests.erl index c12aa5a6..9a89a7c2 100644 --- a/test/riak_repl_schema_tests.erl +++ b/test/riak_repl_schema_tests.erl @@ -18,7 +18,7 @@ basic_schema_test() -> cuttlefish_unit:assert_config(Config, "riak_repl.max_fssource_node", 1), cuttlefish_unit:assert_config(Config, "riak_repl.max_fssink_node", 1), cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_on_connect", true), - cuttlefish_unit:assert_not_configured(Config, "riak_repl.fullsync_interval"), + cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_interval", 360), cuttlefish_unit:assert_config(Config, "riak_repl.rtq_max_bytes", 104857600), cuttlefish_unit:assert_config(Config, "riak_repl.proxy_get", disabled), cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_interval", 15), @@ -31,16 +31,17 @@ override_schema_test() -> Conf = [ {["mdc", "data_root"], "/some/repl/place"}, {["mdc", "cluster_manager"], {"4.3.2.1", 4321}}, - {["mdc", "max_fssource_cluster"], 10}, - {["mdc", "max_fssource_node"], 2}, - {["mdc", "max_fssink_node"], 4}, - {["mdc", "fullsync_on_connect"], false}, - {["mdc", "fullsync_interval", "cluster1"], "15m"}, - {["mdc", "fullsync_interval", "cluster2"], "1h"}, - {["mdc", "rtq_max_bytes"], "50MB"}, + {["mdc", "fullsync", "source", "max_workers_per_cluster"], 10}, + {["mdc", "fullsync", "source", "max_workers_per_node"], 2}, + {["mdc", "fullsync", "sink", "max_workers_per_node"], 4}, + {["mdc", "fullsync", "start_on_connect"], off}, + {["mdc", "fullsync", "interval"], per_sink}, + {["mdc", "fullsync", "interval", "cluster1"], "15m"}, + {["mdc", "fullsync", "interval", "cluster2"], "1h"}, + {["mdc", "realtime", "queue_max_bytes"], "50MB"}, {["mdc", "proxy_get"], on}, - {["mdc", "realtime", "heartbeat_interval"], "15m"}, - {["mdc", "realtime", "heartbeat_timeout"], "15d"} + {["mdc", "realtime", "heartbeat", "interval"], "15m"}, + {["mdc", "realtime", "heartbeat", "timeout"], "15d"} ], %% The defaults are defined in ../priv/riak_repl.schema. From fcb2644da32f1e3772945384818beda7fed7fbba Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Mon, 15 Sep 2014 11:43:16 -0500 Subject: [PATCH 04/37] Add tests for fullsync interval translation --- rebar.config | 1 + test/riak_repl_schema_tests.erl | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/rebar.config b/rebar.config index 8218f10d..24e512c4 100644 --- a/rebar.config +++ b/rebar.config @@ -7,6 +7,7 @@ {erl_first_files, ["src/gen_leader.erl"]}. {xref_checks, []}. {xref_queries, [{"(XC - UC) || (XU - X - B - cluster_info : Mod)", []}]}. +{eunit_opts, [verbose]}. {deps, [ {lager, "2.0.3", {git, "git://github.com/basho/lager.git", {tag, "2.0.3"}}}, diff --git a/test/riak_repl_schema_tests.erl b/test/riak_repl_schema_tests.erl index 9a89a7c2..66b877e4 100644 --- a/test/riak_repl_schema_tests.erl +++ b/test/riak_repl_schema_tests.erl @@ -63,6 +63,42 @@ override_schema_test() -> cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_timeout", 1296000), ok. +fullsync_interval_test_() -> + [ + {"interval=never but sink intervals set results in warning", + fun() -> + riak_repl_lager_test_backend:bounce(warning), + Conf = [{["mdc", "fullsync", "interval"], never}, + {["mdc", "fullsync", "interval", "cluster1"], 360}], + Config = cuttlefish_unit:generate_templated_config(["../priv/riak_repl.schema"], Conf, context()), + cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_interval", disabled), + Logs = riak_repl_lager_test_backend:get_logs(), + ?assertMatch([_|_], Logs), + [ ?assertEqual("mdc.fullsync.interval is set to never, sink specific intervals are ignored", + Message) || [_Time, " ", ["[","warning","]"], Message] <- Logs ] + end}, + {"interval=per_sink but no sink intervals is invalid", + fun() -> + Conf = [{["mdc", "fullsync", "interval"], per_sink}], + Config = cuttlefish_unit:generate_templated_config(["../priv/riak_repl.schema"], Conf, context()), + cuttlefish_unit:assert_error_message(Config, + "Translation for 'riak_repl.fullsync_interval' found invalid configuration: " + "Cannot set mdc.fullsync.interval = per_sink and" + " omit sink-specific intervals, set sink-specific" + " intervals or use 'never'") + end}, + {"interval=Time and per-sink intervals is invalid", + fun() -> + Conf = [{["mdc", "fullsync", "interval"], 60}, + {["mdc", "fullsync", "interval", "cluster1"], 360}], + Config = cuttlefish_unit:generate_templated_config(["../priv/riak_repl.schema"], Conf, context()), + cuttlefish_unit:assert_error_message(Config, + "Translation for 'riak_repl.fullsync_interval' found invalid configuration: " + "Cannot set both mdc.fullsync.interval and sink-specific intervals") + end} + ]. + + %% this context() represents the substitution variables that rebar %% will use during the build process. riak_jmx's schema file is %% written with some {{mustache_vars}} for substitution during From b420ba6c0dd03682ba16f5eb11b776b09b126955 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Mon, 15 Sep 2014 11:48:22 -0500 Subject: [PATCH 05/37] Add heartbeat interval test covering the translation --- test/riak_repl_schema_tests.erl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/riak_repl_schema_tests.erl b/test/riak_repl_schema_tests.erl index 66b877e4..5536d6d8 100644 --- a/test/riak_repl_schema_tests.erl +++ b/test/riak_repl_schema_tests.erl @@ -63,6 +63,12 @@ override_schema_test() -> cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_timeout", 1296000), ok. +heartbeat_interval_test() -> + Conf = [{["mdc", "realtime", "heartbeat"], false}, + {["mdc", "realtime", "heartbeat", "interval"], 300}], + Config = cuttlefish_unit:generate_templated_config(["../priv/riak_repl.schema"], Conf, context()), + cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_interval", undefined). + fullsync_interval_test_() -> [ {"interval=never but sink intervals set results in warning", @@ -99,6 +105,7 @@ fullsync_interval_test_() -> ]. + %% this context() represents the substitution variables that rebar %% will use during the build process. riak_jmx's schema file is %% written with some {{mustache_vars}} for substitution during From 297560b41c6d702c22b3729c03530a7ed904d1a4 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Mon, 15 Sep 2014 11:48:47 -0500 Subject: [PATCH 06/37] Missed committing lager backend in tests --- test/riak_repl_lager_test_backend.erl | 130 ++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 test/riak_repl_lager_test_backend.erl diff --git a/test/riak_repl_lager_test_backend.erl b/test/riak_repl_lager_test_backend.erl new file mode 100644 index 00000000..8f4247bb --- /dev/null +++ b/test/riak_repl_lager_test_backend.erl @@ -0,0 +1,130 @@ +-module(riak_repl_lager_test_backend). + +-behavior(gen_event). + +%% gen_event callbacks +-export([init/1, + handle_call/2, + handle_event/2, + handle_info/2, + terminate/2, + code_change/3]). +-export([get_logs/0, + bounce/0, + bounce/1]). + +%% holds the log messages for retreival on terminate +-record(state, {level :: {mask, integer()}, + verbose :: boolean(), + log = [] :: list()}). + +-include_lib("lager/include/lager.hrl"). + +-spec get_logs() -> [iolist()] | {error, term()}. +get_logs() -> + gen_event:call(lager_event, ?MODULE, get_logs, infinity). + +bounce() -> + bounce(error). + +bounce(Level) -> + application:stop(lager), + lager:start(), + gen_event:add_handler(lager_event, ?MODULE, [error, false]), + lager:set_loglevel(?MODULE, Level), + ok. + +-spec(init(integer()|atom()|[term()]) -> {ok, #state{}} | {error, atom()}). +%% @private +%% @doc Initializes the event handler +init(Level) when is_atom(Level) -> + case lists:member(Level, ?LEVELS) of + true -> + {ok, #state{level=lager_util:level_to_num(Level), verbose=false}}; + _ -> + {error, bad_log_level} + end; +init([Level, Verbose]) -> + case lists:member(Level, ?LEVELS) of + true -> + {ok, #state{level=lager_util:level_to_num(Level), verbose=Verbose}}; + _ -> + {error, bad_log_level} + end. + +-spec(handle_event(tuple(), #state{}) -> {ok, #state{}}). +%% @private +%% @doc handles the event, adding the log message to the gen_event's state. +%% this function attempts to handle logging events in both the simple tuple +%% and new record (introduced after lager 1.2.1) formats. +handle_event({log, Dest, Level, {Date, Time}, [LevelStr, Location, Message]}, %% lager 1.2.1 + #state{level=L, verbose=Verbose, log = Logs} = State) when Level > L -> + case lists:member(riak_test_lager_backend, Dest) of + true -> + Log = case Verbose of + true -> + [Date, " ", Time, " ", LevelStr, Location, Message]; + _ -> + [Time, " ", LevelStr, Message] + end, + {ok, State#state{log=[Log|Logs]}}; + false -> + {ok, State} + end; +handle_event({log, Level, {Date, Time}, [LevelStr, Location, Message]}, %% lager 1.2.1 + #state{level=LogLevel, verbose=Verbose, log = Logs} = State) when Level =< LogLevel -> + Log = case Verbose of + true -> + [Date, " ", Time, " ", LevelStr, Location, Message]; + _ -> + [Time, " ", LevelStr, Message] + end, + {ok, State#state{log=[Log|Logs]}}; +handle_event({log, {lager_msg, Dest, _Meta, Level, DateTime, _Timestamp, Message}}, + State) -> %% lager 2.0.0 + case lager_util:level_to_num(Level) of + L when L =< State#state.level -> + handle_event({log, L, DateTime, + [["[",atom_to_list(Level),"] "], " ", Message]}, + State); + L -> + handle_event({log, Dest, L, DateTime, + [["[",atom_to_list(Level),"] "], " ", Message]}, + State) + end; +handle_event(Event, State) -> + {ok, State#state{log = [Event|State#state.log]}}. + +-spec(handle_call(any(), #state{}) -> {ok, any(), #state{}}). +%% @private +%% @doc gets and sets loglevel. This is part of the lager backend api. +handle_call(get_loglevel, #state{level=Level} = State) -> + {ok, Level, State}; +handle_call({set_loglevel, Level}, State) -> + case lists:member(Level, ?LEVELS) of + true -> + {ok, ok, State#state{level=lager_util:level_to_num(Level)}}; + _ -> + {ok, {error, bad_log_level}, State} + end; +handle_call(get_logs, #state{log = Logs} = State) -> + {ok, lists:reverse(Logs), State}; +handle_call(_, State) -> + {ok, ok, State}. + +-spec(handle_info(any(), #state{}) -> {ok, #state{}}). +%% @private +%% @doc gen_event callback, does nothing. +handle_info(_, State) -> + {ok, State}. + +-spec(code_change(any(), #state{}, any()) -> {ok, #state{}}). +%% @private +%% @doc gen_event callback, does nothing. +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +-spec(terminate(any(), #state{}) -> {ok, list()}). +%% @doc gen_event callback, does nothing. +terminate(_Reason, #state{log=Logs}) -> + {ok, lists:reverse(Logs)}. From 45d130ca6ade6cff5382daba0e5213f4f2d415c0 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Mon, 15 Sep 2014 11:58:11 -0500 Subject: [PATCH 07/37] Cleanup translation with better pattern matching --- priv/riak_repl.schema | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/priv/riak_repl.schema b/priv/riak_repl.schema index 023fd2a1..2077879f 100644 --- a/priv/riak_repl.schema +++ b/priv/riak_repl.schema @@ -82,24 +82,22 @@ "riak_repl.fullsync_interval", fun(Conf) -> FullSyncIntervals = cuttlefish_variable:filter_by_prefix("mdc.fullsync.interval", Conf), - Partitioned = lists:partition(fun({I, _}) -> ["mdc", "fullsync", "interval"] == I end, FullSyncIntervals), - case Partitioned of - {[{_, never}], Sinks} -> - if length(Sinks) > 0 -> - cuttlefish:warn("mdc.fullsync.interval is set to never," - " sink-specific intervals are ignored"); - true -> ok - end, + {[{_, Global}], Sinks} = lists:partition(fun({I, _}) -> ["mdc", "fullsync", "interval"] == I end, FullSyncIntervals), + if Global == never, Sinks == [] -> disabled; - {[{_, Minutes}], []} when is_integer(Minutes) -> - Minutes; - {[{_, per_sink}], []} -> + Global == never -> + cuttlefish:warn("mdc.fullsync.interval is set to never," + " sink-specific intervals are ignored"), + disabled; + is_integer(Global), Sinks == [] -> + Global; + Global == per_sink, Sinks == [] -> cuttlefish:invalid("Cannot set mdc.fullsync.interval = per_sink and" " omit sink-specific intervals, set sink-specific" " intervals or use 'never'"); - {[{_, per_sink}], Sinks} -> + Global == per_sink -> [{list_to_atom(SinkName), Value} || {["mdc", "fullsync", "interval", SinkName], Value} <- Sinks ]; - _ -> + true -> cuttlefish:invalid("Cannot set both mdc.fullsync.interval and" " sink-specific intervals") end From 938b971f2e581cf6253b7be4f4f2b71b875271b7 Mon Sep 17 00:00:00 2001 From: CV/SC Date: Mon, 15 Sep 2014 16:45:04 -0500 Subject: [PATCH 08/37] Add assertions to cover all settings --- test/riak_repl_schema_tests.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/riak_repl_schema_tests.erl b/test/riak_repl_schema_tests.erl index 5536d6d8..e4405239 100644 --- a/test/riak_repl_schema_tests.erl +++ b/test/riak_repl_schema_tests.erl @@ -23,6 +23,8 @@ basic_schema_test() -> cuttlefish_unit:assert_config(Config, "riak_repl.proxy_get", disabled), cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_interval", 15), cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_timeout", 15), + cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_use_background_manager", false), + cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_stat_refresh_interval", 60000), ok. override_schema_test() -> @@ -41,7 +43,9 @@ override_schema_test() -> {["mdc", "realtime", "queue_max_bytes"], "50MB"}, {["mdc", "proxy_get"], on}, {["mdc", "realtime", "heartbeat", "interval"], "15m"}, - {["mdc", "realtime", "heartbeat", "timeout"], "15d"} + {["mdc", "realtime", "heartbeat", "timeout"], "15d"}, + {["mdc", "fullsync", "background_manager"], on}, + {["mdc", "fullsync", "source", "metrics_refresh_interval"], "30s"} ], %% The defaults are defined in ../priv/riak_repl.schema. @@ -61,6 +65,8 @@ override_schema_test() -> cuttlefish_unit:assert_config(Config, "riak_repl.proxy_get", enabled), cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_interval", 900), cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_timeout", 1296000), + cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_use_background_manager", true), + cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_stat_refresh_interval", 30000), ok. heartbeat_interval_test() -> From 15dcb1c9ad22d1d88aa4402f4fd84c53536b734b Mon Sep 17 00:00:00 2001 From: SC/CV/BH Date: Tue, 30 Sep 2014 14:14:36 -0500 Subject: [PATCH 09/37] Implement new repl console UI to match new config --- priv/commands.txt | 20 ++- src/riak_repl_console.erl | 305 +++++++++++++++++++++++++++++++++++++- 2 files changed, 322 insertions(+), 3 deletions(-) diff --git a/priv/commands.txt b/priv/commands.txt index 1a3f1a6d..c9a440f9 100644 --- a/priv/commands.txt +++ b/priv/commands.txt @@ -83,7 +83,7 @@ Usage: riak-repl nat-map [ ...] Sub-commands: add Add a NAT mapping del Delete a NAT mapping - show Display the NAT mapping table + show Display the NAT mapping table Usage: riak-repl nat-map add [:] @@ -127,3 +127,21 @@ Usage: riak-repl proxy-get redirect [ ... ] delete Delete an existing proxy-get redirection show Show a proxy-get redirection cluster-id Display the local cluster's identifier + +Usage: riak-repl proxy-get redirect add + + Add a proxy-get redirection to a new cluster. Arguments + and must correspond to the result + from the `riak-repl proxy-get redirect cluster-id` command. + +Usage: riak-repl proxy-get redirect delete + + Delete a proxy-get redirection. Arguments and + must correspond to the result from the `riak-repl + proxy-get redirect cluster-id` command. + +Usage: riak-repl proxy-get redirect show + + Show an existing proxy-get redirection. Argument + must correspond to the result from the `riak-repl proxy-get redirect + cluster-id` command. diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index 6f044fb7..26dccf9f 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -37,10 +37,307 @@ show_block_provider_redirect/1, show_local_cluster_id/1, delete_block_provider_redirect/1 - ]). + ]). + +-export([command/1]). + +-spec command([string()]) -> ok | error. +command([_Script, "status"]) -> + %% TODO: how does 'quiet' even work? + status([]); +command([_Script, "add-listener"|[_,_,_]=Params]) -> + add_listener(Params); +command([_Script, "add-nat-listener"|[_,_,_,_,_]=Params]) -> + add_nat_listener(Params); +command([_Script, "del-listener"|[_,_,_]=Params]) -> + del_listener(Params); +command([_Script, "add-site"|[_,_,_]=Params]) -> + add_site(Params); +command([_Script, "del-site"|[_]=Params]) -> + del_site(Params); +command([_Script, "start-fullsync"]) -> + start_fullsync([]); +command([_Script, "cancel-fullsync"]) -> + cancel_fullsync([]); +command([_Script, "pause-fullsync"]) -> + pause_fullsync([]); +command([_Script, "resume-fullsync"]) -> + resume_fullsync([]); +command([_Script, "clusterstats"|Params]) when length(Params) =< 1 -> + clusterstats(Params); +command([_Script, "clustername"|Params]) when length(Params) =< 1 -> + clustername(Params); +command([_Script, "connections"]) -> + connections([]); +command([_Script, "clusters"]) -> + clusters([]); +command([_Script, "connect"|[_]=Params]) -> + connect(Params); +command([_Script, "disconnect"|[_]=Params]) -> + disconnect(Params); +command([_Script, "modes"|Params]) -> + modes(Params); +command([_Script, "realtime"|["enable",_]=Params]) -> + realtime(Params); +command([_Script, "realtime"|["disable",_]=Params]) -> + realtime(Params); +command([_Script, "realtime"|["start"|_]=Params]) when length(Params) =< 2 -> + realtime(Params); +command([_Script, "realtime"|["stop"|_]=Params]) when length(Params) =< 2 -> + realtime(Params); +command([_Script, "realtime", "cascades"|[_]=Params]) -> + realtime_cascades(Params); +command([_Script, "fullsync"|["enable",_]=Params]) -> + fullsync(Params); +command([_Script, "fullsync"|["disable",_]=Params]) -> + fullsync(Params); +command([_Script, "fullsync"|["start"|_]=Params]) when length(Params) =< 2 -> + fullsync(Params); +command([_Script, "fullsync"|["stop"|_]=Params]) when length(Params) =< 2 -> + fullsync(Params); +command([_Script, "fullsync", "source","max_workers_per_cluster"|[_]=Params]) -> + max_fssource_cluster(Params); +command([_Script, "fullsync", "source", "max_workers_per_node"|[_]=Params]) -> + max_fssource_node(Params); +command([_Script, "fullsync", "sink", "max_workers_per_node"|[_]=Params]) -> + max_fssink_node(Params); +command([_Script, "proxy-get"|["enable",_]=Params]) -> + proxy_get(Params); +command([_Script, "proxy-get"|["disable",_]=Params]) -> + proxy_get(Params); +command([_Script, "proxy-get", "redirect", "show"|[_]=Params]) -> + show_block_provider_redirect(Params); +command([_Script, "proxy-get", "redirect", "add"|[_,_]=Params]) -> + add_block_provider_redirect(Params); +command([_Script, "proxy-get", "redirect", "delete"|[_]=Params]) -> + delete_block_provider_redirect(Params); +command([_Script, "proxy-get", "redirect", "cluster-id"]) -> + show_local_cluster_id([]); +command([_Script, "nat-map", "show"]) -> + show_nat_map([]); +command([_Script, "nat-map", "add"|[_,_]=Params]) -> + add_nat_map(Params); +command([_Script, "nat-map", "delete"|[_,_]=Params]) -> + del_nat_map(Params); +command([Script|Params]) -> + usage(Script, Params), + error. + +-spec usage_out(string(), iodata()) -> ok. +usage_out(Script, Desc) -> + io:format(standard_error, "Usage: ~s ~s~n", [Script, Desc]). + +-spec usage(string(), [string()]) -> ok. +usage(Script, ["add-listener"|_]) -> + warn_v2_repl(), + usage_out(Script, "add-listener "); +usage(Script, ["add-nat-listener"|_]) -> + warn_v2_repl(), + usage_out(Script, "add-nat-listener "); +usage(Script, ["del-listener"|_]) -> + warn_v2_repl(), + usage_out(Script, "del-listener "); +usage(Script, ["add-site"|_]) -> + warn_v2_repl(), + usage_out(Script, "add-site "); +usage(Script, ["del-site"|_]) -> + warn_v2_repl(), + usage_out(Script, "del-site "); +usage(Script, [V2CMD|_]) when V2CMD == "start-fullsync"; + V2CMD == "cancel-fullsync"; + V2CMD == "pause-fullsync"; + V2CMD == "resume-fullsync" -> + warn_v2_repl(), + usage(Script, []); +usage(Script, ["clusterstats"|_]) -> + usage_out(Script, "clusterstats [ | : ]"); +usage(Script, ["clustername"|_]) -> + usage_out(Script, "clustername [ ]"); +usage(Script, ["connect"|_]) -> + usage_out(Script, "connect :"); +usage(Script, ["disconnect"|_]) -> + usage_out(Script, "disconnect ( : | )"); +usage(Script, ["modes"|_]) -> + usage_out(Script, "modes [ ... ]"); +usage(Script, ["realtime"|Params]) -> + realtime_usage(Script, Params); +usage(Script, ["fullsync"|Params]) -> + fullsync_usage(Script, Params); +usage(Script, ["proxy-get"|Params]) -> + proxy_get_usage(Script, Params); +usage(Script, ["nat-map"|Params]) -> + nat_map_usage(Script, Params); +usage(Script, _) -> + EnabledModes = get_modes(), + ModeHelp = [{mode_repl13, + " Version 3 Commands:\n" + " clustername Show or set the cluster name\n" + " clusterstats Display cluster stats\n" + " connect Connect to a remote cluster\n" + " connections Display a list of connections\n" + " disconnect Disconnect from a remote cluster\n" + " fullsync Manipulate fullsync replication\n" + " nat-map Manipulate NAT mappings\n" + " proxy-get Manipulate proxy-get\n" + " realtime Manipulate realtime replication\n\n"}, + {mode_repl12, + " Version 2 Commands:\n" + " add-listener Add a sink listener\n" + " add-nat-listener Add a sink listener with NAT\n" + " add-site Add a sink site\n" + " cancel-fullsync Cancel running fullsync replication\n" + " del-listener Delete a sink listener\n" + " del-site Delete a sink site\n" + " pause-fullsync Pause running fullsync replication\n" + " resume-fullsync Resume paused fullsync replication\n" + " start-fullsync Start fullsync replication\n\n"}], + ModesCommands = [ Commands || {Mode, Commands} <- ModeHelp, + lists:member(Mode, EnabledModes) ], + usage_out(Script, + [" [ ...]\n\n", + " Commands:\n" + " modes Show or set replication modes\n" + " status Display status and metrics\n\n", + ModesCommands]). + +realtime_usage(Script, ["cascades"|_]) -> + usage_out(Script, + ["realtime cascades \n\n" + " Manipulate cascading realtime replication. When this cluster is a\n" + " sink and is receiving realtime replication, it can propagate\n" + " incoming writes to any clusters for which it is a source and\n" + " realtime replication is enabled.\n\n" + " Sub-commands:\n" + " enable Enable cascading realtime replication\n" + " disable Disable cascading realtime replication\n" + " show Show the current cascading realtime replication setting"] + ); +realtime_usage(Script, _) -> + usage_out(Script, + ["realtime [ ...]\n\n" + " Manipulate realtime replication. Realtime replication streams\n" + " incoming writes on the source cluster to the sink cluster(s).\n\n" + " Sub-commands:" + " enable Enable realtime replication" + " disable Disable realtime replication" + " start Start realtime replication" + " stop Stop realtime replication" + " cascades Manipulate cascading realtime replication"] + ). + +fullsync_usage(Script, ["max_fssink_node"|_]) -> + fullsync_usage(Script, ["sink"]); +fullsync_usage(Script, ["max_fssource_node"|_]) -> + fullsync_usage(Script, ["source"]); +fullsync_usage(Script, ["max_fssource_cluster"|_]) -> + fullsync_usage(Script, ["source"]); +fullsync_usage(Script, ["source"|_]) -> + usage_out(Script, + ["fullsync source []\n\n" + " Set limits on the number of fullsync workers on a source cluster. If\n" + " is omitted, the current setting is displayed.\n\n" + " Available settings:\n" + " max_workers_per_node\n" + " max_workers_per_cluster" + ]); +fullsync_usage(Script, ["sink"|_]) -> + usage_out(Script, + ["fullsync sink max_workers_per_node []\n\n" + " Set limits on the number of fullsync workers on a sink cluster. If\n" + " is omitted, the current setting is displayed." + ]); +fullsync_usage(Script, _) -> + usage_out(Script, + ["fullsync [ ...]\n\n" + " Manipulate fullsync replication. Fullsync replication compares data\n" + " on the source and the sink and then sends detected differences to\n" + " the sink cluster.\n\n" + " Sub-commands:\n" + " enable Enable fullsync replication\n" + " disable Disable fullsync replication\n" + " start Start fullsync replication\n" + " stop Stop fullsync replication\n" + " source Manipulate source cluster limits\n" + " sink Manipulate sink cluster limits" + ]). + +proxy_get_usage(Script, ["enable"|_]) -> + usage_out(Script, + ["proxy-get enable \n\n" + " Enables proxy-get requests from to this source\n" + " cluster."]); +proxy_get_usage(Script, ["disable"|_]) -> + usage_out(Script, + ["proxy-get disable \n\n" + " Disables proxy-get requests from to this source\n" + " cluster."]); +proxy_get_usage(Script, ["redirect", "add"|_]) -> + usage_out(Script, + ["proxy-get redirect add \n\n" + " Add a proxy-get redirection to a new cluster. Arguments\n" + " and must correspond to the result\n" + " from the `", Script, " proxy-get redirect cluster-id` command.\n" + ]); +proxy_get_usage(Script, ["redirect", "del"|_]) -> + usage_out(Script, + ["proxy-get redirect delete \n\n" + " Delete a proxy-get redirection. Arguments and\n" + " must correspond to the result from the `", Script, "\n", + " proxy-get redirect cluster-id` command." + ]); +proxy_get_usage(Script, ["redirect", "show"|_]) -> + usage_out(Script, + ["proxy-get redirect show \n\n" + " Show an existing proxy-get redirection. Argument \n" + " must correspond to the result from the `", Script, "proxy-get redirect\n" + " cluster-id` command." + ]); +proxy_get_usage(Script, ["redirect"|_]) -> + usage_out(Script, + ["proxy-get redirect [ ... ]\n\n" + " Manipulate proxy-get redirection functionality. Redirection allows\n" + " existing proxy-get connections to be redirected to new source\n" + " clusters so that the original source cluster can be decommissioned.\n\n" + " Sub-commands:\n" + " add Add a proxy-get redirection\n" + " delete Delete an existing proxy-get redirection\n" + " show Show a proxy-get redirection\n" + " cluster-id Display the local cluster's identifier"]); +proxy_get_usage(Script, _) -> + usage_out(Script, + ["proxy-get [ ... ]\n\n" + " Manipulate proxy-get functionality. Proxy-get allows sink clusters\n" + " to actively fetch remote objects over a realtime replication\n" + " connection. Currently, this is only used by Riak CS.\n" + " Sub-commands:\n" + " enable Enable proxy-get on the source\n" + " disable Disable proxy-get on the source\n" + " redirect Manipulation proxy-get redirection"]). + +nat_map_usage(Script, ["add"|_]) -> + usage_out(Script, + ["nat-map add [:] \n\n" + " Add a NAT mapping from the given external IP to the given internal" + " IP. An optional external port can be supplied."]); +nat_map_usage(Script, ["delete"|_]) -> + usage_out(Script, + ["nat-map delete [:] \n\n" + " Delete a NAT mapping between the given external IP and the given" + " internal IP."]); +nat_map_usage(Script, _) -> + usage_out(Script, + ["nat-map [ ...]\n\n" + " Manipulate NAT mappings. NAT mappings allow replication connections\n" + " to traverse firewalls between private networks on previously\n" + " configured ports.\n\n" + " Sub-commands:\n" + " add Add a NAT mapping\n" + " delete Delete a NAT mapping\n" + " show Display the NAT mapping table" + ]). add_listener(Params) -> - lager:warning(?V2REPLDEP, []), + warn_v2_repl(), Ring = get_ring(), case add_listener_internal(Ring,Params) of {ok, NewRing} -> @@ -48,6 +345,10 @@ add_listener(Params) -> error -> error end. +warn_v2_repl() -> + io:format(?V2REPLDEP, []), + lager:warning(?V2REPLDEP, []). + add_nat_listener(Params) -> lager:warning(?V2REPLDEP, []), Ring = get_ring(), From 2f91e19df0ba434a186e8d78e3d6086577847009 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Tue, 30 Sep 2014 14:15:25 -0500 Subject: [PATCH 10/37] Formatting fixes --- src/riak_repl_console.erl | 256 +++++++++++++++++++------------------- 1 file changed, 128 insertions(+), 128 deletions(-) diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index 26dccf9f..4a2c438c 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -487,25 +487,25 @@ cluster_fs_running(Sink) -> start_fullsync([]) -> lager:warning(?V2REPLDEP, []), _ = [riak_repl_tcp_server:start_fullsync(Pid) || - Pid <- riak_repl_listener_sup:server_pids()], + Pid <- riak_repl_listener_sup:server_pids()], ok. cancel_fullsync([]) -> lager:warning(?V2REPLDEP, []), _ = [riak_repl_tcp_server:cancel_fullsync(Pid) || - Pid <- riak_repl_listener_sup:server_pids()], + Pid <- riak_repl_listener_sup:server_pids()], ok. pause_fullsync([]) -> lager:warning(?V2REPLDEP, []), _ = [riak_repl_tcp_server:pause_fullsync(Pid) || - Pid <- riak_repl_listener_sup:server_pids()], + Pid <- riak_repl_listener_sup:server_pids()], ok. resume_fullsync([]) -> lager:warning(?V2REPLDEP, []), _ = [riak_repl_tcp_server:resume_fullsync(Pid) || - Pid <- riak_repl_listener_sup:server_pids()], + Pid <- riak_repl_listener_sup:server_pids()], ok. @@ -523,11 +523,11 @@ cluster_mgr_stats() -> case erlang:whereis(riak_repl_leader_gs) of Pid when is_pid(Pid) -> ConnectedClusters = case riak_core_cluster_mgr:get_known_clusters() of - {ok, Clusters} -> - [erlang:list_to_binary(Cluster) || Cluster <- - Clusters]; - Error -> Error - end, + {ok, Clusters} -> + [erlang:list_to_binary(Cluster) || Cluster <- + Clusters]; + Error -> Error + end, [{cluster_name, erlang:list_to_binary(riak_core_connection:symbolic_clustername())}, {cluster_leader, riak_core_cluster_mgr:get_leader()}, @@ -712,15 +712,15 @@ realtime([Cmd, Remote]) -> realtime([Cmd]) -> Remotes = riak_repl2_rt:enabled(), _ = case Cmd of - "start" -> - ?LOG_USER_CMD("Start Realtime Replication to all connected clusters", - []), - _ = [riak_repl2_rt:start(Remote) || Remote <- Remotes]; - "stop" -> - ?LOG_USER_CMD("Stop Realtime Replication to all connected clusters", - []), - _ = [riak_repl2_rt:stop(Remote) || Remote <- Remotes] - end, + "start" -> + ?LOG_USER_CMD("Start Realtime Replication to all connected clusters", + []), + _ = [riak_repl2_rt:start(Remote) || Remote <- Remotes]; + "stop" -> + ?LOG_USER_CMD("Stop Realtime Replication to all connected clusters", + []), + _ = [riak_repl2_rt:stop(Remote) || Remote <- Remotes] + end, ok. fullsync([Cmd, Remote]) -> @@ -729,13 +729,13 @@ fullsync([Cmd, Remote]) -> "enable" -> ?LOG_USER_CMD("Enable Fullsync Replication to cluster ~p", [Remote]), riak_core_ring_manager:ring_trans(fun - riak_repl_ring:fs_enable_trans/2, Remote), + riak_repl_ring:fs_enable_trans/2, Remote), _ = riak_repl2_fscoordinator_sup:start_coord(Leader, Remote), ok; "disable" -> ?LOG_USER_CMD("Disable Fullsync Replication to cluster ~p", [Remote]), riak_core_ring_manager:ring_trans(fun - riak_repl_ring:fs_disable_trans/2, Remote), + riak_repl_ring:fs_disable_trans/2, Remote), _ = riak_repl2_fscoordinator_sup:stop_coord(Leader, Remote), ok; "start" -> @@ -769,12 +769,12 @@ fullsync([Cmd]) -> "start" -> ?LOG_USER_CMD("Start Fullsync Replication to all connected clusters",[]), _ = [riak_repl2_fscoordinator:start_fullsync(Pid) || {_, Pid} <- - Fullsyncs], + Fullsyncs], ok; "stop" -> ?LOG_USER_CMD("Stop Fullsync Replication to all connected clusters",[]), _ = [riak_repl2_fscoordinator:stop_fullsync(Pid) || {_, Pid} <- - Fullsyncs], + Fullsyncs], ok end, ok. @@ -784,12 +784,12 @@ proxy_get([Cmd, Remote]) -> "enable" -> ?LOG_USER_CMD("Enable Riak CS Proxy GET block provider for ~p",[Remote]), riak_core_ring_manager:ring_trans(fun - riak_repl_ring:pg_enable_trans/2, Remote), + riak_repl_ring:pg_enable_trans/2, Remote), ok; "disable" -> ?LOG_USER_CMD("Disable Riak CS Proxy GET block provider for ~p",[Remote]), riak_core_ring_manager:ring_trans(fun - riak_repl_ring:pg_disable_trans/2, Remote), + riak_repl_ring:pg_disable_trans/2, Remote), ok end. @@ -806,11 +806,11 @@ modes(NewModes) -> realtime_cascades(["always"]) -> ?LOG_USER_CMD("Enable Realtime Replication cascading", []), riak_core_ring_manager:ring_trans(fun - riak_repl_ring:rt_cascades_trans/2, always); + riak_repl_ring:rt_cascades_trans/2, always); realtime_cascades(["never"]) -> ?LOG_USER_CMD("Disable Realtime Replication cascading", []), riak_core_ring_manager:ring_trans(fun - riak_repl_ring:rt_cascades_trans/2, never); + riak_repl_ring:rt_cascades_trans/2, never); realtime_cascades([]) -> Cascades = app_helper:get_env(riak_repl, realtime_cascades, always), io:format("realtime_cascades: ~p~n", [Cascades]); @@ -857,7 +857,7 @@ max_fssource_cluster(NewVal) -> max_fssink_node([]) -> io:format("max_fssink_node value = ~p~n", - [app_helper:get_env(riak_repl, max_fssink_node, ?DEFAULT_MAX_SINKS_NODE)]); + [app_helper:get_env(riak_repl, max_fssink_node, ?DEFAULT_MAX_SINKS_NODE)]); max_fssink_node([FSSinkNode]) -> NewVal = erlang:list_to_integer(FSSinkNode), riak_core_util:rpc_every_member(?MODULE, max_fssink_node, [NewVal], ?CONSOLE_RPC_TIMEOUT), @@ -872,12 +872,12 @@ show_nat_map([]) -> Ring = get_ring(), io:format("Nat map: ~n"), [io:format(" ~-21.. s -> ~s~n", - [print_ip_and_maybe_port(Int), print_ip_and_maybe_port(Ext)]) - || {Int, Ext} <- riak_repl_ring:get_nat_map(Ring)]. + [print_ip_and_maybe_port(Int), print_ip_and_maybe_port(Ext)]) + || {Int, Ext} <- riak_repl_ring:get_nat_map(Ring)]. add_nat_map([External, Internal]) -> case {parse_ip_and_maybe_port(External, false), - parse_ip_and_maybe_port(Internal, true)} of + parse_ip_and_maybe_port(Internal, true)} of {{error, Reason}, _} -> io:format("Bad external IP ~p", [Reason]), error; @@ -887,14 +887,14 @@ add_nat_map([External, Internal]) -> {ExternalIP, InternalIP} -> ?LOG_USER_CMD("Add a NAT map from External IP ~p to Internal IP ~p", [ExternalIP, InternalIP]), riak_core_ring_manager:ring_trans( - fun riak_repl_ring:add_nat_map/2, - {ExternalIP, InternalIP}), + fun riak_repl_ring:add_nat_map/2, + {ExternalIP, InternalIP}), ok end. del_nat_map([External, Internal]) -> case {parse_ip_and_maybe_port(External, false), - parse_ip_and_maybe_port(Internal, true)} of + parse_ip_and_maybe_port(Internal, true)} of {{error, Reason}, _} -> io:format("Bad external IP ~p", [Reason]), error; @@ -904,19 +904,19 @@ del_nat_map([External, Internal]) -> {ExternalIP, InternalIP} -> ?LOG_USER_CMD("Delete a NAT map from External IP ~p to Internal IP ~p", [ExternalIP, InternalIP]), riak_core_ring_manager:ring_trans( - fun riak_repl_ring:del_nat_map/2, - {ExternalIP, InternalIP}), + fun riak_repl_ring:del_nat_map/2, + {ExternalIP, InternalIP}), ok end. -% NB: the following commands are around the "Dead Cluster" redirect feature, -% 306. They all operate using cluster_id (tuple), not clustername, for now, as -% of this writing we had no reliable way to map a clustername to an id -% over disterlang. When this API becomes available, this feature may use -% it. +%% NB: the following commands are around the "Dead Cluster" redirect feature, +%% 306. They all operate using cluster_id (tuple), not clustername, for now, as +%% of this writing we had no reliable way to map a clustername to an id +%% over disterlang. When this API becomes available, this feature may use +%% it. add_block_provider_redirect([FromClusterId, ToClusterId]) -> lager:info("Redirecting cluster id: ~p to ~p", [FromClusterId, ToClusterId]), - riak_core_metadata:put({<<"replication">>, <<"cluster-mapping">>}, + riak_core_metadata:put({<<"replication">>, <<"cluster-mapping">>}, FromClusterId, ToClusterId). show_block_provider_redirect([FromClusterId]) -> @@ -934,7 +934,7 @@ delete_block_provider_redirect([FromClusterId]) -> show_local_cluster_id([]) -> {ok, Ring} = riak_core_ring_manager:get_my_ring(), ClusterId = lists:flatten( - io_lib:format("~p", [riak_core_ring:cluster_name(Ring)])), + io_lib:format("~p", [riak_core_ring:cluster_name(Ring)])), io:format("local cluster id: ~p~n", [ClusterId]). %% helper functions @@ -991,7 +991,7 @@ extract_rt_fs_send_recv_kbps(Most) -> FSSendKbps = sum_fs_send_kbps(Most), FSRecvKbps = sum_fs_recv_kbps(Most), [{realtime_send_kbps, RTSendKbps}, {realtime_recv_kbps, RTRecvKbps}, - {fullsync_send_kbps, FSSendKbps}, {fullsync_recv_kbps, FSRecvKbps}]. + {fullsync_send_kbps, FSSendKbps}, {fullsync_recv_kbps, FSRecvKbps}]. print_ip_and_maybe_port({IP, Port}) when is_tuple(IP) -> [inet_parse:ntoa(IP), $:, integer_to_list(Port)]; @@ -1006,13 +1006,13 @@ format_counter_stats([]) -> ok; format_counter_stats([{K,V}|T]) when is_list(K) -> io:format("~s: ~p~n", [K,V]), format_counter_stats(T); -%format_counter_stats([{K,V}|T]) when K == fullsync_coordinator -> -% io:format("V = ~p",[V]), -% case V of -% [] -> io:format("~s: {}~n",[K]); -% Val -> io:format("~s: ~s",[K,Val]) -% end, -% format_counter_stats(T); +%%format_counter_stats([{K,V}|T]) when K == fullsync_coordinator -> +%% io:format("V = ~p",[V]), +%% case V of +%% [] -> io:format("~s: {}~n",[K]); +%% Val -> io:format("~s: ~s",[K,Val]) +%% end, +%% format_counter_stats(T); format_counter_stats([{K,V}|T]) when K == client_rx_kbps; K == client_tx_kbps; K == server_rx_kbps; @@ -1033,8 +1033,8 @@ make_listener(NodeName, IP, Port) -> make_nat_listener(NodeName, IP, Port, PublicIP, PublicPort) -> #nat_listener{nodename=list_to_atom(NodeName), - listen_addr={IP, list_to_integer(Port)}, - nat_addr={PublicIP, list_to_integer(PublicPort)}}. + listen_addr={IP, list_to_integer(Port)}, + nat_addr={PublicIP, list_to_integer(PublicPort)}}. make_site(SiteName, IP, Port) -> @@ -1066,18 +1066,18 @@ get_config() -> {ok, Sites} -> lists:flatten([format_site(S) || S <- Sites]) end ++ - case dict:find(listeners, Repl) of - error -> - []; - {ok, Listeners} -> - lists:flatten([format_listener(L) || L <- Listeners]) - end ++ - case dict:find(natlisteners, Repl) of - error -> - []; - {ok, NatListeners} -> - lists:flatten([format_nat_listener(L) || L <- NatListeners]) - end + case dict:find(listeners, Repl) of + error -> + []; + {ok, Listeners} -> + lists:flatten([format_listener(L) || L <- Listeners]) + end ++ + case dict:find(natlisteners, Repl) of + error -> + []; + {ok, NatListeners} -> + lists:flatten([format_nat_listener(L) || L <- NatListeners]) + end end. format_site(S) -> @@ -1096,36 +1096,36 @@ format_listener(L) -> format_nat_listener(L) -> [{"natlistener_" ++ atom_to_list(L#nat_listener.nodename), format_ip(L#nat_listener.listen_addr) ++ "->" ++ - format_ip(L#nat_listener.nat_addr)}]. + format_ip(L#nat_listener.nat_addr)}]. leader_stats() -> case erlang:whereis(riak_repl_leader_gs) of Pid when is_pid(Pid) -> LeaderNode = riak_repl_leader:leader_node(), LocalStats = - try - LocalProcInfo = erlang:process_info(whereis(riak_repl_leader_gs), - [message_queue_len, heap_size]), - [{"local_leader_" ++ atom_to_list(K), V} || {K,V} <- LocalProcInfo] - catch _:_ -> - [] - end, + try + LocalProcInfo = erlang:process_info(whereis(riak_repl_leader_gs), + [message_queue_len, heap_size]), + [{"local_leader_" ++ atom_to_list(K), V} || {K,V} <- LocalProcInfo] + catch _:_ -> + [] + end, RemoteStats = - try - LeaderPid = rpc:call(LeaderNode, erlang, whereis, - [riak_repl_leader_gs]), - LeaderStats = rpc:call(LeaderNode, erlang, process_info, - [LeaderPid, [message_queue_len, - total_heap_size, - heap_size, - stack_size, - reductions, - garbage_collection]]), - [{"leader_" ++ atom_to_list(K), V} || {K,V} <- LeaderStats] - catch - _:_ -> - [] - end, + try + LeaderPid = rpc:call(LeaderNode, erlang, whereis, + [riak_repl_leader_gs]), + LeaderStats = rpc:call(LeaderNode, erlang, process_info, + [LeaderPid, [message_queue_len, + total_heap_size, + heap_size, + stack_size, + reductions, + garbage_collection]]), + [{"leader_" ++ atom_to_list(K), V} || {K,V} <- LeaderStats] + catch + _:_ -> + [] + end, [{leader, LeaderNode}] ++ RemoteStats ++ LocalStats; _ -> [] end. @@ -1140,7 +1140,7 @@ client_stats() -> client_stats_rpc() -> RT2 = [rt2_sink_stats(P) || P <- riak_repl2_rt:get_sink_pids()] ++ - [fs2_sink_stats(P) || P <- riak_repl2_fssink_sup:started()], + [fs2_sink_stats(P) || P <- riak_repl2_fssink_sup:started()], Pids = [P || {_,P,_,_} <- supervisor:which_children(riak_repl_client_sup), P /= undefined], [{client_stats, [client_stats(P) || P <- Pids]}, {sinks, RT2}]. @@ -1148,7 +1148,7 @@ server_stats() -> case erlang:whereis(riak_repl_leader_gs) of Pid when is_pid(Pid) -> RT2 = [rt2_source_stats(P) || {_R,P} <- - riak_repl2_rtsource_conn_sup:enabled()], + riak_repl2_rtsource_conn_sup:enabled()], LeaderNode = riak_repl_leader:leader_node(), case LeaderNode of undefined -> @@ -1219,7 +1219,7 @@ rt2_source_stats(Pid) -> end, FormattedPid = riak_repl_util:safe_pid_to_list(Pid), {source_stats, [{pid,FormattedPid}, erlang:process_info(Pid, message_queue_len), - {rt_source_connected_to, State}]}. + {rt_source_connected_to, State}]}. rt2_sink_stats(Pid) -> Timeout = app_helper:get_env(riak_repl, status_timeout, 5000), @@ -1232,18 +1232,18 @@ rt2_sink_stats(Pid) -> %%{Pid, erlang:process_info(Pid, message_queue_len), State}. FormattedPid = riak_repl_util:safe_pid_to_list(Pid), {sink_stats, [{pid,FormattedPid}, erlang:process_info(Pid, message_queue_len), - {rt_sink_connected_to, State}]}. + {rt_sink_connected_to, State}]}. fs2_sink_stats(Pid) -> Timeout = app_helper:get_env(riak_repl, status_timeout, 5000), State = try - %% even though it's named legacy_status, it's BNW code - riak_repl2_fssink:legacy_status(Pid, Timeout) - catch - _:_ -> - too_busy - end, - %% {Pid, erlang:process_info(Pid, message_queue_len), State}. + %% even though it's named legacy_status, it's BNW code + riak_repl2_fssink:legacy_status(Pid, Timeout) + catch + _:_ -> + too_busy + end, + %% {Pid, erlang:process_info(Pid, message_queue_len), State}. {sink_stats, [{pid,riak_repl_util:safe_pid_to_list(Pid)}, erlang:process_info(Pid, message_queue_len), {fs_connected_to, State}]}. @@ -1258,14 +1258,14 @@ sum_rt_kbps(Stats, KbpsDirection) -> Sinks = proplists:get_value(sinks, Stats, []), Sources = proplists:get_value(sources, Stats, []), Kbpss = lists:foldl(fun({StatKind, SinkProps}, Acc) -> - Path1 = case StatKind of - sink_stats -> rt_sink_connected_to; - source_stats -> rt_source_connected_to; - _Else -> not_found - end, - KbpsStr = proplists_get([Path1, socket, KbpsDirection], SinkProps, "[]"), - get_first_kbsp(KbpsStr) + Acc - end, 0, Sinks ++ Sources), + Path1 = case StatKind of + sink_stats -> rt_sink_connected_to; + source_stats -> rt_source_connected_to; + _Else -> not_found + end, + KbpsStr = proplists_get([Path1, socket, KbpsDirection], SinkProps, "[]"), + get_first_kbsp(KbpsStr) + Acc + end, 0, Sinks ++ Sources), Kbpss. sum_fs_send_kbps(Stats) -> @@ -1277,40 +1277,40 @@ sum_fs_recv_kbps(Stats) -> sum_fs_kbps(Stats, Direction) -> Coordinators = proplists:get_value(fullsync_coordinator, Stats), CoordFoldFun = fun({_SinkName, FSCoordStats}, Acc) -> - CoordKbpsStr = proplists_get([socket, Direction], FSCoordStats, "[]"), - CoordKbps = get_first_kbsp(CoordKbpsStr), - CoordSourceKpbs = sum_fs_source_kbps(FSCoordStats, Direction), - SinkKbps = sum_fs_sink_kbps(Stats, Direction), - Acc + CoordKbps + CoordSourceKpbs + SinkKbps - end, + CoordKbpsStr = proplists_get([socket, Direction], FSCoordStats, "[]"), + CoordKbps = get_first_kbsp(CoordKbpsStr), + CoordSourceKpbs = sum_fs_source_kbps(FSCoordStats, Direction), + SinkKbps = sum_fs_sink_kbps(Stats, Direction), + Acc + CoordKbps + CoordSourceKpbs + SinkKbps + end, CoordSrvs = proplists:get_value(fullsync_coordinator_srv, Stats), CoordSrvsFoldFun = fun({_IPPort, SrvStats}, Acc) -> - KbpsStr = proplists_get([socket, Direction], SrvStats, "[]"), - Kbps = get_first_kbsp(KbpsStr), - Kbps + Acc - end, + KbpsStr = proplists_get([socket, Direction], SrvStats, "[]"), + Kbps = get_first_kbsp(KbpsStr), + Kbps + Acc + end, lists:foldl(CoordFoldFun, 0, Coordinators) + lists:foldl(CoordSrvsFoldFun, 0, CoordSrvs). sum_fs_source_kbps(CoordStats, Direction) -> Running = proplists:get_value(running_stats, CoordStats, []), FoldFun = fun({_Pid, Stats}, Acc) -> - KbpsStr = proplists_get([socket, Direction], Stats, "[]"), - Kbps = get_first_kbsp(KbpsStr), - Acc + Kbps - end, + KbpsStr = proplists_get([socket, Direction], Stats, "[]"), + Kbps = get_first_kbsp(KbpsStr), + Acc + Kbps + end, lists:foldl(FoldFun, 0, Running). sum_fs_sink_kbps(Stats, Direction) -> Sinks = proplists:get_value(sinks, Stats, []), FoldFun = fun({sink_stats, SinkStats}, Acc) -> - case proplists_get([fs_connected_to, socket, Direction], SinkStats) of - undefined -> - Acc; - KbpsStr -> - Kbps = get_first_kbsp(KbpsStr), - Acc + Kbps - end - end, + case proplists_get([fs_connected_to, socket, Direction], SinkStats) of + undefined -> + Acc; + KbpsStr -> + Kbps = get_first_kbsp(KbpsStr), + Acc + Kbps + end + end, lists:foldl(FoldFun, 0, Sinks). proplists_get(Path, Props) -> From 72aaf8692d35429b21866440eec5e60a05965a2a Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Tue, 30 Sep 2014 15:07:53 -0500 Subject: [PATCH 11/37] Fix output formatting * `standard_error` group leader cannot be used because it is not forwarded to the RPC caller. * Fix newlines around top-level usage and V2 warnings --- src/riak_repl_console.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index 4a2c438c..6f9ea083 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -125,7 +125,7 @@ command([Script|Params]) -> -spec usage_out(string(), iodata()) -> ok. usage_out(Script, Desc) -> - io:format(standard_error, "Usage: ~s ~s~n", [Script, Desc]). + io:format("Usage: ~s ~s~n", [Script, Desc]). -spec usage(string(), [string()]) -> ok. usage(Script, ["add-listener"|_]) -> @@ -179,7 +179,7 @@ usage(Script, _) -> " fullsync Manipulate fullsync replication\n" " nat-map Manipulate NAT mappings\n" " proxy-get Manipulate proxy-get\n" - " realtime Manipulate realtime replication\n\n"}, + " realtime Manipulate realtime replication"}, {mode_repl12, " Version 2 Commands:\n" " add-listener Add a sink listener\n" @@ -190,9 +190,9 @@ usage(Script, _) -> " del-site Delete a sink site\n" " pause-fullsync Pause running fullsync replication\n" " resume-fullsync Resume paused fullsync replication\n" - " start-fullsync Start fullsync replication\n\n"}], - ModesCommands = [ Commands || {Mode, Commands} <- ModeHelp, - lists:member(Mode, EnabledModes) ], + " start-fullsync Start fullsync replication"}], + ModesCommands = string:join([ Commands || {Mode, Commands} <- ModeHelp, + lists:member(Mode, EnabledModes) ], "\n\n"), usage_out(Script, [" [ ...]\n\n", " Commands:\n" @@ -346,7 +346,7 @@ add_listener(Params) -> end. warn_v2_repl() -> - io:format(?V2REPLDEP, []), + io:format(?V2REPLDEP++"~n~n", []), lager:warning(?V2REPLDEP, []). add_nat_listener(Params) -> From 60216c6e05c025cb2d01ec02d045ba4a45fbf35c Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Tue, 30 Sep 2014 15:09:21 -0500 Subject: [PATCH 12/37] Give some output when running fullsync commands --- src/riak_repl_console.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index 6f9ea083..16f25ec2 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -488,24 +488,28 @@ start_fullsync([]) -> lager:warning(?V2REPLDEP, []), _ = [riak_repl_tcp_server:start_fullsync(Pid) || Pid <- riak_repl_listener_sup:server_pids()], + io:format("Fullsync started~n"), ok. cancel_fullsync([]) -> lager:warning(?V2REPLDEP, []), _ = [riak_repl_tcp_server:cancel_fullsync(Pid) || Pid <- riak_repl_listener_sup:server_pids()], + io:format("Fullsync canceled~n"), ok. pause_fullsync([]) -> lager:warning(?V2REPLDEP, []), _ = [riak_repl_tcp_server:pause_fullsync(Pid) || Pid <- riak_repl_listener_sup:server_pids()], + io:format("Fullsync paused~n"), ok. resume_fullsync([]) -> lager:warning(?V2REPLDEP, []), _ = [riak_repl_tcp_server:resume_fullsync(Pid) || Pid <- riak_repl_listener_sup:server_pids()], + io:format("Fullsync resumed~n"), ok. From 9a7f94a5fbbf29c35a0b3794f37dfbe378e1eeaf Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Wed, 1 Oct 2014 08:59:47 -0500 Subject: [PATCH 13/37] Fix usage output of realtime command. --- src/riak_repl_console.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index 16f25ec2..db7f72cb 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -217,11 +217,11 @@ realtime_usage(Script, _) -> ["realtime [ ...]\n\n" " Manipulate realtime replication. Realtime replication streams\n" " incoming writes on the source cluster to the sink cluster(s).\n\n" - " Sub-commands:" - " enable Enable realtime replication" - " disable Disable realtime replication" - " start Start realtime replication" - " stop Stop realtime replication" + " Sub-commands:\n" + " enable Enable realtime replication\n" + " disable Disable realtime replication\n" + " start Start realtime replication\n" + " stop Stop realtime replication\n" " cascades Manipulate cascading realtime replication"] ). From b38244ba36bf9ea60e0e8cbb31fded0537bec241 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Wed, 1 Oct 2014 10:32:38 -0500 Subject: [PATCH 14/37] Make commands more explicit in a few cases --- src/riak_repl_console.erl | 50 ++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index db7f72cb..e3e04488 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -75,7 +75,9 @@ command([_Script, "connect"|[_]=Params]) -> connect(Params); command([_Script, "disconnect"|[_]=Params]) -> disconnect(Params); -command([_Script, "modes"|Params]) -> +command([_Script, "modes", "show"]) -> + modes([]); +command([_Script, "modes", "set"|Params]) when length(Params) >= 1 -> modes(Params); command([_Script, "realtime"|["enable",_]=Params]) -> realtime(Params); @@ -95,11 +97,17 @@ command([_Script, "fullsync"|["start"|_]=Params]) when length(Params) =< 2 -> fullsync(Params); command([_Script, "fullsync"|["stop"|_]=Params]) when length(Params) =< 2 -> fullsync(Params); -command([_Script, "fullsync", "source","max_workers_per_cluster"|[_]=Params]) -> +command([_Script, "fullsync", "source", "max_workers_per_cluster", "show"]) -> + max_fssource_cluster([]); +command([_Script, "fullsync", "source", "max_workers_per_cluster", "set"|[_]=Params]) -> max_fssource_cluster(Params); -command([_Script, "fullsync", "source", "max_workers_per_node"|[_]=Params]) -> +command([_Script, "fullsync", "source", "max_workers_per_node", "show"]) -> + max_fssource_node([]); +command([_Script, "fullsync", "source", "max_workers_per_node", "set"|[_]=Params]) -> max_fssource_node(Params); -command([_Script, "fullsync", "sink", "max_workers_per_node"|[_]=Params]) -> +command([_Script, "fullsync", "sink", "max_workers_per_node", "show"]) -> + max_fssink_node([]); +command([_Script, "fullsync", "sink", "max_workers_per_node", "set"|[_]=Params]) -> max_fssink_node(Params); command([_Script, "proxy-get"|["enable",_]=Params]) -> proxy_get(Params); @@ -157,8 +165,12 @@ usage(Script, ["connect"|_]) -> usage_out(Script, "connect :"); usage(Script, ["disconnect"|_]) -> usage_out(Script, "disconnect ( : | )"); -usage(Script, ["modes"|_]) -> - usage_out(Script, "modes [ ... ]"); +usage(Script, ["modes"|_]) -> + usage_out(Script, + "modes [ ... ]\n\n" + " Sub-commands:\n" + " show Show the active replication modes\n" + " set Set the active replication modes"); usage(Script, ["realtime"|Params]) -> realtime_usage(Script, Params); usage(Script, ["fullsync"|Params]) -> @@ -807,19 +819,23 @@ modes(NewModes) -> set_modes(Modes), modes([]). -realtime_cascades(["always"]) -> +realtime_cascades(["enable"]) -> ?LOG_USER_CMD("Enable Realtime Replication cascading", []), - riak_core_ring_manager:ring_trans(fun - riak_repl_ring:rt_cascades_trans/2, always); -realtime_cascades(["never"]) -> + riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, + always), + io:format("Realtime cascades enabled.~n"); +realtime_cascades(["disable"]) -> ?LOG_USER_CMD("Disable Realtime Replication cascading", []), - riak_core_ring_manager:ring_trans(fun - riak_repl_ring:rt_cascades_trans/2, never); -realtime_cascades([]) -> - Cascades = app_helper:get_env(riak_repl, realtime_cascades, always), - io:format("realtime_cascades: ~p~n", [Cascades]); -realtime_cascades(_Wut) -> - io:format("realtime_cascades either \"always\" or \"never\"~n"). + riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, + never), + io:format("Realtime cascades disabled.~n"); +realtime_cascades(["show"]) -> + case app_helper:get_env(riak_repl, realtime_cascades, always) of + always -> + io:format("Realtime cascades are enabled.~n"); + never -> + io:format("Realtime cascades are disabled.~n") + end. cascades(Val) -> realtime_cascades(Val). From fbc0d1a3333c3e2b2a876440928926336bdf4915 Mon Sep 17 00:00:00 2001 From: CV/SC Date: Wed, 1 Oct 2014 16:26:07 -0500 Subject: [PATCH 15/37] Add fullsync strategy to replication schema --- priv/riak_repl.schema | 8 ++++++++ test/riak_repl_schema_tests.erl | 3 +++ 2 files changed, 11 insertions(+) diff --git a/priv/riak_repl.schema b/priv/riak_repl.schema index 2077879f..441317cb 100644 --- a/priv/riak_repl.schema +++ b/priv/riak_repl.schema @@ -19,6 +19,13 @@ {datatype, ip} ]}. +%% @doc The fullsync strategy to use. +{mapping, "mdc.fullsync.strategy", "riak_repl.fullsync_strategy", +[{datatype, {enum, [keylist, aae]}}, + {default, keylist}, + hidden +]}. + %% @doc The limit of fullsync workers running per source-side of a %% replication connection. For example, if you have a cluster that is %% replicating to 3 sink clusters and this set to 5, you will have at @@ -172,3 +179,4 @@ {default, off}, hidden ]}. + diff --git a/test/riak_repl_schema_tests.erl b/test/riak_repl_schema_tests.erl index e4405239..b18c7bd1 100644 --- a/test/riak_repl_schema_tests.erl +++ b/test/riak_repl_schema_tests.erl @@ -25,6 +25,7 @@ basic_schema_test() -> cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_timeout", 15), cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_use_background_manager", false), cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_stat_refresh_interval", 60000), + cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_strategy", keylist), ok. override_schema_test() -> @@ -33,6 +34,7 @@ override_schema_test() -> Conf = [ {["mdc", "data_root"], "/some/repl/place"}, {["mdc", "cluster_manager"], {"4.3.2.1", 4321}}, + {["mdc", "fullsync", "strategy"], aae}, {["mdc", "fullsync", "source", "max_workers_per_cluster"], 10}, {["mdc", "fullsync", "source", "max_workers_per_node"], 2}, {["mdc", "fullsync", "sink", "max_workers_per_node"], 4}, @@ -67,6 +69,7 @@ override_schema_test() -> cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_timeout", 1296000), cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_use_background_manager", true), cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_stat_refresh_interval", 30000), + cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_strategy", aae), ok. heartbeat_interval_test() -> From fe8144e8200aa3a90aa51fd4a831a3d42e4b2b49 Mon Sep 17 00:00:00 2001 From: CV/SC Date: Wed, 1 Oct 2014 16:30:50 -0500 Subject: [PATCH 16/37] Add experimental schema. These settings were extracted from the replication code, but have not been vetted or necessarily named properly. Many of them are slated to be removed or changed for future versions of riak_repl. To promote a setting to the main schema, one should write documentation and tests to cover the setting. --- priv/riak_repl_experimental.schema | 163 +++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 priv/riak_repl_experimental.schema diff --git a/priv/riak_repl_experimental.schema b/priv/riak_repl_experimental.schema new file mode 100644 index 00000000..8dc30675 --- /dev/null +++ b/priv/riak_repl_experimental.schema @@ -0,0 +1,163 @@ +%%-*- mode: erlang -*- +%% ============ Experimental / Transient Settings ============ + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting. +{mapping, "mdc.cluster_manager.cancellation_interval", + "riak_repl.cm_cancellation_interval", + [{datatype, {duration, ms}}, + hidden + ]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting. +{mapping, "mdc.cluster_manager.no_endpoints_retry_interval", + "riak_repl.connmgr_no_endpoint_retry", + [{datatype, {duration, ms}}, + hidden + ]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.status_timeout", "riak_repl.status_timeout", +[{datatype, {duration, ms}}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.status_helper_timeout", "riak_repl.status_helper_timeout", +[{datatype, {duration, ms}}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.replicate_cs_bucket_objects", "riak_repl.replicate_cs_bucket_objects", +[{datatype, flag}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.replicate_cs_user_objects", "riak_repl.replicate_cs_user_objects", +[{datatype, flag}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.fullsync.sink.min_put_workers", "riak_repl.fssink_min_workers", +[{datatype, integer}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.fullsync.sink.max_put_workers", "riak_repl.fssink_max_workers", +[{datatype, integer}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.realtime.queue.overload_threshold", "riak_repl.rtq_overload_threshold", +[{datatype, integer}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.realtime.queue.overload_recover", "riak_repl.rtq_overload_recover", +[{datatype, integer}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.fullsync.source.max_retries", "riak_repl.max_fssource_retries", +[{datatype, [{atom, infinity}, integer]}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.fullsync.max_reserve_retries", "riak_repl.max_reserve_retries", +[{datatype, integer}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.realtime.queue.drop_report_interval", "riak_repl.rtq_drop_report_interval", +[{datatype, {duration, ms}}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.realtime.sink.max_pending", "riak_repl.rtsink_max_pending", +[ + %% TODO: Jon says size this according to the rtsink_max_workers + %% setting + {datatype, integer}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.realtime.sink.min_put_workers", "riak_repl.rtsink_min_workers", +[{datatype, integer}, + {default, 5}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.realtime.sink.max_put_workers", "riak_repl.rtsink_max_workers", +[{datatype, integer}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.realtime.sink.bucket_type_drop_report_interval", "riak_repl.bucket_type_drop_report_interval", +[{datatype, {duration, ms}}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.realtime.socket_reactivate_interval", "riak_repl.reactivate_socket_interval_millis", +[{datatype, {duration, ms}}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.realtime.source.helper_status_timeout", "riak_repl.riak_repl2_rtsource_helper_status_to", +[{datatype, {duration, ms}}, + hidden +]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.fullsync.aae.direct.limit", "riak_repl.fullsync_direct_limit", + [{datatype, integer}, + hidden]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.fullsync.aae.direct.mode", "riak_repl.fullsync_direct_mode", + [{datatype, {enum, [inline, buffered]}}, + hidden]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.fullsync.keylist.min_get_workers", "riak_repl.min_get_workers", + [{datatype, integer}, + hidden]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.fullsync.keylist.max_get_workers", "riak_repl.max_get_workers", + [{datatype, integer}, + hidden]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.fullsync.keylist.vnode_gets", "riak_repl.vnode_gets", + [{datatype, flag}, + hidden]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.fullsync.keylist.diff_batch_size", "riak_repl.diff_batch_size", + [{datatype, integer}, + hidden]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.fullsync.keylist.bloom_fold", "riak_repl.bloom_fold", + [{datatype, flag}, + hidden]}. + +%% @doc EXPERIMENTAL:: Consult with Basho about changing this setting +{mapping, "mdc.realtime.queue_migration_timeout", "riak_repl.queue_migration_timeout", +[{datatype, {duration, ms}}, + hidden +]}. From 0ad7bd45f3f7a54e45253f5208e8e2bd83d3c34b Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Thu, 2 Oct 2014 10:57:18 -0500 Subject: [PATCH 17/37] Remove commands.txt stub --- priv/commands.txt | 147 ---------------------------------------------- 1 file changed, 147 deletions(-) delete mode 100644 priv/commands.txt diff --git a/priv/commands.txt b/priv/commands.txt deleted file mode 100644 index c9a440f9..00000000 --- a/priv/commands.txt +++ /dev/null @@ -1,147 +0,0 @@ -Usage: riak-repl [ ...] [ ...] - - Commands: - modes Show or set replication modes - status Display status and metrics - - Version 3 Commands: - clustername Show or set the cluster name - clusterstats Display cluster stats - connect Connect to a remote cluster - connections Display a list of connections - disconnect Disconnect from a remote cluster - fullsync Manipulate fullsync replication - nat-map Manipulate NAT mappings - proxy-get Manipulate proxy-get - realtime Manipulate realtime replication - - Version 2 Commands: - add-listener Add a sink listener - add-nat-listener Add a sink listener with NAT - add-site Add a sink site - cancel-fullsync Cancel running fullsync replication - del-listener Delete a sink listener - del-site Delete a sink site - pause-fullsync Pause running fullsync replication - resume-fullsync Resume paused fullsync replication - start-fullsync Start fullsync replication - -Usage: riak-repl realtime [ ...] - - Manipulate realtime replication. Realtime replication streams - incoming writes on the source cluster to the sink cluster(s). - - Sub-commands: - enable Enable realtime replication - disable Disable realtime replication - start Start realtime replication - stop Stop realtime replication - cascades Manipulate cascading realtime replication - -Usage: riak-repl realtime cascades - - Manipulate cascading realtime replication. When this cluster is a - sink and is receiving realtime replication, it can propagate - incoming writes to any clusters for which it is a source and - realtime replication is enabled. - - Sub-commands: - enable Enable cascading realtime replication - disable Disable cascading realtime replication - show Show the current cascading realtime replication setting - -Usage: riak-repl fullsync [ ...] - - Manipulate fullsync replication. Fullsync replication compares data - on the source and the sink and then sends detected differences to - the sink cluster. - - Sub-commands: - enable Enable fullsync replication - disable Disable fullsync replication - start Start fullsync replication - stop Stop fullsync replication - source Manipulate source cluster limits - sink Manipulate sink cluster limits - -Usage: riak-repl fullsync source (max_workers_per_node | max_workers_per_cluster) [] - - Set limits on the number of fullsync workers on a source cluster. If - is omitted, the current setting is displayed. - -Usage: riak-repl fullsync sink max_workers_per_node [] - - Set limits on the number of fullsync workers on a sink cluster. If - is omitted, the current setting is displayed. - -Usage: riak-repl nat-map [ ...] - - Manipulate NAT mappings. NAT mappings allow replication connections - to traverse firewalls between private networks on previously - configured ports. - - Sub-commands: - add Add a NAT mapping - del Delete a NAT mapping - show Display the NAT mapping table - -Usage: riak-repl nat-map add [:] - - Add a NAT mapping from the given external IP to the given internal - IP. An optional external port can be supplied. - -Usage: riak-repl nat-map del [:] - - Delete a NAT mapping between the given external IP and the given - internal IP. - -Usage: riak-repl proxy-get [ ... ] - - Manipulate proxy-get functionality. Proxy-get allows sink clusters - to actively fetch remote objects over a realtime replication - connection. Currently, this is only used by Riak CS. - - Sub-commands: - enable Enable proxy-get on the source - disable Disable proxy-get on the source - redirect Manipulation proxy-get redirection - -Usage: riak-repl proxy-get enable - - Enables proxy-get requests from to this source - cluster. - -Usage: riak-repl proxy-get disable - - Disables proxy-get requests from to this source - cluster. - -Usage: riak-repl proxy-get redirect [ ... ] - - Manipulate proxy-get redirection functionality. Redirection allows - existing proxy-get connections to be redirected to new source - clusters so that the original source cluster can be decommissioned. - - Sub-commands: - add Add a proxy-get redirection - delete Delete an existing proxy-get redirection - show Show a proxy-get redirection - cluster-id Display the local cluster's identifier - -Usage: riak-repl proxy-get redirect add - - Add a proxy-get redirection to a new cluster. Arguments - and must correspond to the result - from the `riak-repl proxy-get redirect cluster-id` command. - -Usage: riak-repl proxy-get redirect delete - - Delete a proxy-get redirection. Arguments and - must correspond to the result from the `riak-repl - proxy-get redirect cluster-id` command. - -Usage: riak-repl proxy-get redirect show - - Show an existing proxy-get redirection. Argument - must correspond to the result from the `riak-repl proxy-get redirect - cluster-id` command. From 09e7a6ca8079b70a8dc13b4a9dd5d19033957274 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Fri, 10 Oct 2014 09:37:44 -0500 Subject: [PATCH 18/37] Fix failure in schema tests --- test/riak_repl_schema_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/riak_repl_schema_tests.erl b/test/riak_repl_schema_tests.erl index b18c7bd1..9ab2778b 100644 --- a/test/riak_repl_schema_tests.erl +++ b/test/riak_repl_schema_tests.erl @@ -24,7 +24,7 @@ basic_schema_test() -> cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_interval", 15), cuttlefish_unit:assert_config(Config, "riak_repl.rt_heartbeat_timeout", 15), cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_use_background_manager", false), - cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_stat_refresh_interval", 60000), + cuttlefish_unit:assert_not_configured(Config, "riak_repl.fullsync_stat_refresh_interval"), cuttlefish_unit:assert_config(Config, "riak_repl.fullsync_strategy", keylist), ok. From 0ced87dfea9f6f86e3b25e19b72ce9a7d1cf8e49 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Fri, 13 Feb 2015 17:00:05 -0600 Subject: [PATCH 19/37] WIP splitting out v2 and v3 commands, implementing clique --- src/riak_repl_console.erl | 804 +++++++++++++----------------------- src/riak_repl_console12.erl | 206 +++++++++ src/riak_repl_console13.erl | 622 ++++++++++++++++++++++++++++ 3 files changed, 1119 insertions(+), 513 deletions(-) create mode 100644 src/riak_repl_console12.erl create mode 100644 src/riak_repl_console13.erl diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index e3e04488..7328459f 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -1,24 +1,17 @@ %% Riak EnterpriseDS -%% Copyright 2007-2009 Basho Technologies, Inc. All Rights Reserved. +%% Copyright 2007-2015 Basho Technologies, Inc. All Rights Reserved. -module(riak_repl_console). --author('Andy Gross '). -include("riak_repl.hrl"). --export([add_listener/1, del_listener/1, add_nat_listener/1]). --export([add_site/1, del_site/1]). --export([status/1, start_fullsync/1, cancel_fullsync/1, - pause_fullsync/1, resume_fullsync/1]). -export([client_stats_rpc/0, server_stats_rpc/0]). -export([extract_rt_fs_send_recv_kbps/1]). --export([clustername/1, clusters/1,clusterstats/1, - connect/1, disconnect/1, connections/1, - realtime/1, fullsync/1, proxy_get/1 +-export([ + fullsync/1, proxy_get/1 ]). -export([rt_remotes_status/0, fs_remotes_status/0]). -export([get_config/0, - cluster_mgr_stats/0, leader_stats/0, client_stats/0, server_stats/0, @@ -28,7 +21,7 @@ max_fssource_node/1, max_fssource_cluster/1, max_fssink_node/1, - realtime_cascades/1, + %% realtime_cascades/1, cascades/1, show_nat_map/1, add_nat_map/1, @@ -39,124 +32,131 @@ delete_block_provider_redirect/1 ]). +-export([get_ring/0, maybe_set_ring/2]). + -export([command/1]). +-export([script_name/0, register_command/4, register_usage/2]). + +-spec script_name() -> string(). +script_name() -> + case get(script_name) of + undefined -> "riak-repl"; + Script -> Script + end. + +-spec register_command([string()], list(), list(), fun()) -> ok. +register_command(Cmd, Keys, Flags, Fun) -> + clique:register_command(["riak-repl"|Cmd], Keys, Flags, Fun). + +-spec register_usage([string()], iolist() | fun(() -> iolist())) -> ok. +register_usage(Cmd, Usage) -> + UsageFun = fun() -> + UsageStr = if is_function(Usage) -> Usage(); + true -> Usage + end, + [script_name(), " ", UsageStr] + end, + clique:register_usage(["riak-repl"|Cmd], UsageFun). + +%% @doc Entry-point for all riak-repl commands. -spec command([string()]) -> ok | error. -command([_Script, "status"]) -> - %% TODO: how does 'quiet' even work? - status([]); -command([_Script, "add-listener"|[_,_,_]=Params]) -> - add_listener(Params); -command([_Script, "add-nat-listener"|[_,_,_,_,_]=Params]) -> - add_nat_listener(Params); -command([_Script, "del-listener"|[_,_,_]=Params]) -> - del_listener(Params); -command([_Script, "add-site"|[_,_,_]=Params]) -> - add_site(Params); -command([_Script, "del-site"|[_]=Params]) -> - del_site(Params); -command([_Script, "start-fullsync"]) -> - start_fullsync([]); -command([_Script, "cancel-fullsync"]) -> - cancel_fullsync([]); -command([_Script, "pause-fullsync"]) -> - pause_fullsync([]); -command([_Script, "resume-fullsync"]) -> - resume_fullsync([]); -command([_Script, "clusterstats"|Params]) when length(Params) =< 1 -> - clusterstats(Params); -command([_Script, "clustername"|Params]) when length(Params) =< 1 -> - clustername(Params); -command([_Script, "connections"]) -> - connections([]); -command([_Script, "clusters"]) -> - clusters([]); -command([_Script, "connect"|[_]=Params]) -> - connect(Params); -command([_Script, "disconnect"|[_]=Params]) -> - disconnect(Params); -command([_Script, "modes", "show"]) -> - modes([]); -command([_Script, "modes", "set"|Params]) when length(Params) >= 1 -> - modes(Params); -command([_Script, "realtime"|["enable",_]=Params]) -> - realtime(Params); -command([_Script, "realtime"|["disable",_]=Params]) -> - realtime(Params); -command([_Script, "realtime"|["start"|_]=Params]) when length(Params) =< 2 -> - realtime(Params); -command([_Script, "realtime"|["stop"|_]=Params]) when length(Params) =< 2 -> - realtime(Params); -command([_Script, "realtime", "cascades"|[_]=Params]) -> - realtime_cascades(Params); -command([_Script, "fullsync"|["enable",_]=Params]) -> - fullsync(Params); -command([_Script, "fullsync"|["disable",_]=Params]) -> - fullsync(Params); -command([_Script, "fullsync"|["start"|_]=Params]) when length(Params) =< 2 -> - fullsync(Params); -command([_Script, "fullsync"|["stop"|_]=Params]) when length(Params) =< 2 -> - fullsync(Params); -command([_Script, "fullsync", "source", "max_workers_per_cluster", "show"]) -> - max_fssource_cluster([]); -command([_Script, "fullsync", "source", "max_workers_per_cluster", "set"|[_]=Params]) -> - max_fssource_cluster(Params); -command([_Script, "fullsync", "source", "max_workers_per_node", "show"]) -> - max_fssource_node([]); -command([_Script, "fullsync", "source", "max_workers_per_node", "set"|[_]=Params]) -> - max_fssource_node(Params); -command([_Script, "fullsync", "sink", "max_workers_per_node", "show"]) -> - max_fssink_node([]); -command([_Script, "fullsync", "sink", "max_workers_per_node", "set"|[_]=Params]) -> - max_fssink_node(Params); -command([_Script, "proxy-get"|["enable",_]=Params]) -> - proxy_get(Params); -command([_Script, "proxy-get"|["disable",_]=Params]) -> - proxy_get(Params); -command([_Script, "proxy-get", "redirect", "show"|[_]=Params]) -> - show_block_provider_redirect(Params); -command([_Script, "proxy-get", "redirect", "add"|[_,_]=Params]) -> - add_block_provider_redirect(Params); -command([_Script, "proxy-get", "redirect", "delete"|[_]=Params]) -> - delete_block_provider_redirect(Params); -command([_Script, "proxy-get", "redirect", "cluster-id"]) -> - show_local_cluster_id([]); -command([_Script, "nat-map", "show"]) -> - show_nat_map([]); -command([_Script, "nat-map", "add"|[_,_]=Params]) -> - add_nat_map(Params); -command([_Script, "nat-map", "delete"|[_,_]=Params]) -> - del_nat_map(Params); -command([Script|Params]) -> - usage(Script, Params), - error. +command([Script|Args]) -> + %% We stash the script name (which may be a partial or absolute + %% path) in the process dictionary so usage output can grab it + %% later. + put(script_name, Script), + %% We don't really want to touch legacy commands, so try to + %% dispatch them first. + case riak_repl_console12:dispatch(Args) of + %% If there's no matching legacy command (or usage should be + %% printed), try to "upgrade" the arguments to clique-style, + %% then invoke clique. + nomatch -> + NewCmd = riak_repl_console13:upgrade(Args), + clique:run(["riak-repl"|NewCmd]); + OkOrError -> + OkOrError + end. + + + +%% command([_Script, "status"]) -> +%% %% TODO: how does 'quiet' even work? +%% status([]); +%% command([_Script, "clusterstats"|Params]) when length(Params) =< 1 -> +%% clusterstats(Params); +%% command([_Script, "clustername"|Params]) when length(Params) =< 1 -> +%% clustername(Params); +%% command([_Script, "connections"]) -> +%% connections([]); +%% command([_Script, "clusters"]) -> +%% clusters([]); +%% command([_Script, "connect"|[_]=Params]) -> +%% connect(Params); +%% command([_Script, "disconnect"|[_]=Params]) -> +%% disconnect(Params); +%% command([_Script, "modes", "show"]) -> +%% modes([]); +%% command([_Script, "modes", "set"|Params]) when length(Params) >= 1 -> +%% modes(Params); +%% command([_Script, "realtime"|["enable",_]=Params]) -> +%% realtime(Params); +%% command([_Script, "realtime"|["disable",_]=Params]) -> +%% realtime(Params); +%% command([_Script, "realtime"|["start"|_]=Params]) when length(Params) =< 2 -> +%% realtime(Params); +%% command([_Script, "realtime"|["stop"|_]=Params]) when length(Params) =< 2 -> +%% realtime(Params); +%% command([_Script, "realtime", "cascades"|[_]=Params]) -> +%% realtime_cascades(Params); +%% command([_Script, "fullsync"|["enable",_]=Params]) -> +%% fullsync(Params); +%% command([_Script, "fullsync"|["disable",_]=Params]) -> +%% fullsync(Params); +%% command([_Script, "fullsync"|["start"|_]=Params]) when length(Params) =< 2 -> +%% fullsync(Params); +%% command([_Script, "fullsync"|["stop"|_]=Params]) when length(Params) =< 2 -> +%% fullsync(Params); +%% command([_Script, "fullsync", "source", "max_workers_per_cluster", "show"]) -> +%% max_fssource_cluster([]); +%% command([_Script, "fullsync", "source", "max_workers_per_cluster", "set"|[_]=Params]) -> +%% max_fssource_cluster(Params); +%% command([_Script, "fullsync", "source", "max_workers_per_node", "show"]) -> +%% max_fssource_node([]); +%% command([_Script, "fullsync", "source", "max_workers_per_node", "set"|[_]=Params]) -> +%% max_fssource_node(Params); +%% command([_Script, "fullsync", "sink", "max_workers_per_node", "show"]) -> +%% max_fssink_node([]); +%% command([_Script, "fullsync", "sink", "max_workers_per_node", "set"|[_]=Params]) -> +%% max_fssink_node(Params); +%% command([_Script, "proxy-get"|["enable",_]=Params]) -> +%% proxy_get(Params); +%% command([_Script, "proxy-get"|["disable",_]=Params]) -> +%% proxy_get(Params); +%% command([_Script, "proxy-get", "redirect", "show"|[_]=Params]) -> +%% show_block_provider_redirect(Params); +%% command([_Script, "proxy-get", "redirect", "add"|[_,_]=Params]) -> +%% add_block_provider_redirect(Params); +%% command([_Script, "proxy-get", "redirect", "delete"|[_]=Params]) -> +%% delete_block_provider_redirect(Params); +%% command([_Script, "proxy-get", "redirect", "cluster-id"]) -> +%% show_local_cluster_id([]); +%% command([_Script, "nat-map", "show"]) -> +%% show_nat_map([]); +%% command([_Script, "nat-map", "add"|[_,_]=Params]) -> +%% add_nat_map(Params); +%% command([_Script, "nat-map", "delete"|[_,_]=Params]) -> +%% del_nat_map(Params); +%% command([Script|Params]) -> +%% usage(Script, Params), +%% error. -spec usage_out(string(), iodata()) -> ok. usage_out(Script, Desc) -> io:format("Usage: ~s ~s~n", [Script, Desc]). -spec usage(string(), [string()]) -> ok. -usage(Script, ["add-listener"|_]) -> - warn_v2_repl(), - usage_out(Script, "add-listener "); -usage(Script, ["add-nat-listener"|_]) -> - warn_v2_repl(), - usage_out(Script, "add-nat-listener "); -usage(Script, ["del-listener"|_]) -> - warn_v2_repl(), - usage_out(Script, "del-listener "); -usage(Script, ["add-site"|_]) -> - warn_v2_repl(), - usage_out(Script, "add-site "); -usage(Script, ["del-site"|_]) -> - warn_v2_repl(), - usage_out(Script, "del-site "); -usage(Script, [V2CMD|_]) when V2CMD == "start-fullsync"; - V2CMD == "cancel-fullsync"; - V2CMD == "pause-fullsync"; - V2CMD == "resume-fullsync" -> - warn_v2_repl(), - usage(Script, []); usage(Script, ["clusterstats"|_]) -> usage_out(Script, "clusterstats [ | : ]"); usage(Script, ["clustername"|_]) -> @@ -165,14 +165,14 @@ usage(Script, ["connect"|_]) -> usage_out(Script, "connect :"); usage(Script, ["disconnect"|_]) -> usage_out(Script, "disconnect ( : | )"); -usage(Script, ["modes"|_]) -> - usage_out(Script, +usage(Script, ["modes"|_]) -> + usage_out(Script, "modes [ ... ]\n\n" " Sub-commands:\n" " show Show the active replication modes\n" " set Set the active replication modes"); -usage(Script, ["realtime"|Params]) -> - realtime_usage(Script, Params); +%% usage(Script, ["realtime"|Params]) -> +%% realtime_usage(Script, Params); usage(Script, ["fullsync"|Params]) -> fullsync_usage(Script, Params); usage(Script, ["proxy-get"|Params]) -> @@ -181,30 +181,11 @@ usage(Script, ["nat-map"|Params]) -> nat_map_usage(Script, Params); usage(Script, _) -> EnabledModes = get_modes(), - ModeHelp = [{mode_repl13, - " Version 3 Commands:\n" - " clustername Show or set the cluster name\n" - " clusterstats Display cluster stats\n" - " connect Connect to a remote cluster\n" - " connections Display a list of connections\n" - " disconnect Disconnect from a remote cluster\n" - " fullsync Manipulate fullsync replication\n" - " nat-map Manipulate NAT mappings\n" - " proxy-get Manipulate proxy-get\n" - " realtime Manipulate realtime replication"}, - {mode_repl12, - " Version 2 Commands:\n" - " add-listener Add a sink listener\n" - " add-nat-listener Add a sink listener with NAT\n" - " add-site Add a sink site\n" - " cancel-fullsync Cancel running fullsync replication\n" - " del-listener Delete a sink listener\n" - " del-site Delete a sink site\n" - " pause-fullsync Pause running fullsync replication\n" - " resume-fullsync Resume paused fullsync replication\n" - " start-fullsync Start fullsync replication"}], - ModesCommands = string:join([ Commands || {Mode, Commands} <- ModeHelp, - lists:member(Mode, EnabledModes) ], "\n\n"), + ModeHelp = [{mode_repl13, riak_repl_console13}, + {mode_repl12, riak_repl_console12}], + ModesCommands = string:join([ Module:commands_usage() || + {Mode, Module} <- ModeHelp, + lists:member(Mode, EnabledModes) ], "\n\n"), usage_out(Script, [" [ ...]\n\n", " Commands:\n" @@ -212,30 +193,30 @@ usage(Script, _) -> " status Display status and metrics\n\n", ModesCommands]). -realtime_usage(Script, ["cascades"|_]) -> - usage_out(Script, - ["realtime cascades \n\n" - " Manipulate cascading realtime replication. When this cluster is a\n" - " sink and is receiving realtime replication, it can propagate\n" - " incoming writes to any clusters for which it is a source and\n" - " realtime replication is enabled.\n\n" - " Sub-commands:\n" - " enable Enable cascading realtime replication\n" - " disable Disable cascading realtime replication\n" - " show Show the current cascading realtime replication setting"] - ); -realtime_usage(Script, _) -> - usage_out(Script, - ["realtime [ ...]\n\n" - " Manipulate realtime replication. Realtime replication streams\n" - " incoming writes on the source cluster to the sink cluster(s).\n\n" - " Sub-commands:\n" - " enable Enable realtime replication\n" - " disable Disable realtime replication\n" - " start Start realtime replication\n" - " stop Stop realtime replication\n" - " cascades Manipulate cascading realtime replication"] - ). +%% realtime_usage(Script, ["cascades"|_]) -> +%% usage_out(Script, +%% ["realtime cascades \n\n" +%% " Manipulate cascading realtime replication. When this cluster is a\n" +%% " sink and is receiving realtime replication, it can propagate\n" +%% " incoming writes to any clusters for which it is a source and\n" +%% " realtime replication is enabled.\n\n" +%% " Sub-commands:\n" +%% " enable Enable cascading realtime replication\n" +%% " disable Disable cascading realtime replication\n" +%% " show Show the current cascading realtime replication setting"] +%% ); +%% realtime_usage(Script, _) -> +%% usage_out(Script, +%% ["realtime [ ...]\n\n" +%% " Manipulate realtime replication. Realtime replication streams\n" +%% " incoming writes on the source cluster to the sink cluster(s).\n\n" +%% " Sub-commands:\n" +%% " enable Enable realtime replication\n" +%% " disable Disable realtime replication\n" +%% " start Start realtime replication\n" +%% " stop Stop realtime replication\n" +%% " cascades Manipulate cascading realtime replication"] +%% ). fullsync_usage(Script, ["max_fssink_node"|_]) -> fullsync_usage(Script, ["sink"]); @@ -348,88 +329,6 @@ nat_map_usage(Script, _) -> " show Display the NAT mapping table" ]). -add_listener(Params) -> - warn_v2_repl(), - Ring = get_ring(), - case add_listener_internal(Ring,Params) of - {ok, NewRing} -> - ok = maybe_set_ring(Ring, NewRing); - error -> error - end. - -warn_v2_repl() -> - io:format(?V2REPLDEP++"~n~n", []), - lager:warning(?V2REPLDEP, []). - -add_nat_listener(Params) -> - lager:warning(?V2REPLDEP, []), - Ring = get_ring(), - case add_nat_listener_internal(Ring, Params) of - {ok, NewRing} -> - ok = maybe_set_ring(Ring, NewRing); - error -> error - end. - -add_listener_internal(Ring, [NodeName, IP, Port]) -> - Listener = make_listener(NodeName, IP, Port), - case lists:member(Listener#repl_listener.nodename, riak_core_ring:all_members(Ring)) of - true -> - case catch rpc:call(Listener#repl_listener.nodename, - riak_repl_util, valid_host_ip, [IP]) of - true -> - NewRing = riak_repl_ring:add_listener(Ring, Listener), - {ok,NewRing}; - false -> - io:format("~p is not a valid IP address for ~p\n", - [IP, Listener#repl_listener.nodename]), - error; - Error -> - io:format("Node ~p must be available to add listener: ~p\n", - [Listener#repl_listener.nodename, Error]), - error - end; - false -> - io:format("~p is not a member of the cluster\n", [Listener#repl_listener.nodename]), - error - end. - -add_nat_listener_internal(Ring, [NodeName, IP, Port, PublicIP, PublicPort]) -> - case add_listener_internal(Ring, [NodeName, IP, Port]) of - {ok,NewRing} -> - case inet_parse:address(PublicIP) of - {ok, _} -> - NatListener = make_nat_listener(NodeName, IP, Port, PublicIP, PublicPort), - NewRing2 = riak_repl_ring:add_nat_listener(NewRing, NatListener), - {ok, NewRing2}; - {error, IPParseError} -> - io:format("Invalid NAT IP address: ~p~n", [IPParseError]), - error - end; - error -> - io:format("Error adding nat address. ~n"), - error - end. - -del_listener([NodeName, IP, Port]) -> - lager:warning(?V2REPLDEP, []), - Ring = get_ring(), - Listener = make_listener(NodeName, IP, Port), - NewRing0 = riak_repl_ring:del_listener(Ring, Listener), - NewRing = riak_repl_ring:del_nat_listener(NewRing0, Listener), - ok = maybe_set_ring(Ring, NewRing). - -add_site([IP, Port, SiteName]) -> - lager:warning(?V2REPLDEP, []), - Ring = get_ring(), - Site = make_site(SiteName, IP, Port), - NewRing = riak_repl_ring:add_site(Ring, Site), - ok = maybe_set_ring(Ring, NewRing). - -del_site([SiteName]) -> - lager:warning(?V2REPLDEP, []), - Ring = get_ring(), - NewRing = riak_repl_ring:del_site(Ring, SiteName), - ok = maybe_set_ring(Ring, NewRing). set_modes(Modes) -> Ring = get_ring(), @@ -496,248 +395,140 @@ cluster_fs_running(Sink) -> ClusterCoord = riak_repl2_fscoordinator_sup:coord_for_cluster(Sink), riak_repl2_fscoordinator:is_running(ClusterCoord). -start_fullsync([]) -> - lager:warning(?V2REPLDEP, []), - _ = [riak_repl_tcp_server:start_fullsync(Pid) || - Pid <- riak_repl_listener_sup:server_pids()], - io:format("Fullsync started~n"), - ok. - -cancel_fullsync([]) -> - lager:warning(?V2REPLDEP, []), - _ = [riak_repl_tcp_server:cancel_fullsync(Pid) || - Pid <- riak_repl_listener_sup:server_pids()], - io:format("Fullsync canceled~n"), - ok. - -pause_fullsync([]) -> - lager:warning(?V2REPLDEP, []), - _ = [riak_repl_tcp_server:pause_fullsync(Pid) || - Pid <- riak_repl_listener_sup:server_pids()], - io:format("Fullsync paused~n"), - ok. - -resume_fullsync([]) -> - lager:warning(?V2REPLDEP, []), - _ = [riak_repl_tcp_server:resume_fullsync(Pid) || - Pid <- riak_repl_listener_sup:server_pids()], - io:format("Fullsync resumed~n"), - ok. - %% %% Repl2 commands %% -rtq_stats() -> - case erlang:whereis(riak_repl2_rtq) of - Pid when is_pid(Pid) -> - [{realtime_queue_stats, riak_repl2_rtq:status()}]; - _ -> [] - end. - -cluster_mgr_stats() -> - case erlang:whereis(riak_repl_leader_gs) of - Pid when is_pid(Pid) -> - ConnectedClusters = case riak_core_cluster_mgr:get_known_clusters() of - {ok, Clusters} -> - [erlang:list_to_binary(Cluster) || Cluster <- - Clusters]; - Error -> Error - end, - [{cluster_name, - erlang:list_to_binary(riak_core_connection:symbolic_clustername())}, - {cluster_leader, riak_core_cluster_mgr:get_leader()}, - {connected_clusters, ConnectedClusters}]; - _ -> [] - end. - %% Show cluster stats for this node -clusterstats([]) -> - %% connection manager stats - CMStats = cluster_mgr_stats(), - CConnStats = riak_core_connection_mgr_stats:get_consolidated_stats(), - Stats = CMStats ++ CConnStats, - io:format("~p~n", [Stats]); -%% slice cluster stats by remote "IP:Port" or "protocol-id". -%% Example protocol-id is rt_repl -clusterstats([Arg]) -> - NWords = string:words(Arg, $:), - case NWords of - 1 -> - %% assume protocol-id - ProtocolId = list_to_atom(Arg), - CConnStats = riak_core_connection_mgr_stats:get_stats_by_protocol(ProtocolId), - CMStats = cluster_mgr_stats(), - Stats = CMStats ++ CConnStats, - io:format("~p~n", [Stats]); - 2 -> - Address = Arg, - IP = string:sub_word(Address, 1, $:), - PortStr = string:sub_word(Address, 2, $:), - {Port,_Rest} = string:to_integer(PortStr), - CConnStats = riak_core_connection_mgr_stats:get_stats_by_ip({IP,Port}), - CMStats = cluster_mgr_stats(), - Stats = CMStats ++ CConnStats, - io:format("~p~n", [Stats]); - _ -> - {error, {badarg, Arg}} - end. +%% clusterstats([]) -> +%% %% connection manager stats +%% CMStats = cluster_mgr_stats(), +%% CConnStats = riak_core_connection_mgr_stats:get_consolidated_stats(), +%% Stats = CMStats ++ CConnStats, +%% io:format("~p~n", [Stats]); +%% %% slice cluster stats by remote "IP:Port" or "protocol-id". +%% %% Example protocol-id is rt_repl +%% clusterstats([Arg]) -> +%% NWords = string:words(Arg, $:), +%% case NWords of +%% 1 -> +%% %% assume protocol-id +%% ProtocolId = list_to_atom(Arg), +%% CConnStats = riak_core_connection_mgr_stats:get_stats_by_protocol(ProtocolId), +%% CMStats = cluster_mgr_stats(), +%% Stats = CMStats ++ CConnStats, +%% io:format("~p~n", [Stats]); +%% 2 -> +%% Address = Arg, +%% IP = string:sub_word(Address, 1, $:), +%% PortStr = string:sub_word(Address, 2, $:), +%% {Port,_Rest} = string:to_integer(PortStr), +%% CConnStats = riak_core_connection_mgr_stats:get_stats_by_ip({IP,Port}), +%% CMStats = cluster_mgr_stats(), +%% Stats = CMStats ++ CConnStats, +%% io:format("~p~n", [Stats]); +%% _ -> +%% {error, {badarg, Arg}} +%% end. %% TODO: cluster naming belongs in riak_core_ring, not in riak_core_connection, but %% not until we move all of the connection stuff to core. -clustername([]) -> - MyName = riak_core_connection:symbolic_clustername(), - io:format("~s~n", [MyName]), - ok; -clustername([ClusterName]) -> - ?LOG_USER_CMD("Set clustername to ~p", [ClusterName]), - riak_core_ring_manager:ring_trans(fun riak_core_connection:set_symbolic_clustername/2, - ClusterName), - ok. - -clusters([]) -> - {ok, Clusters} = riak_core_cluster_mgr:get_known_clusters(), - lists:foreach( - fun(ClusterName) -> - {ok,Members} = riak_core_cluster_mgr:get_ipaddrs_of_cluster(ClusterName), - IPs = [string_of_ipaddr(Addr) || Addr <- Members], - io:format("~s: ~p~n", [ClusterName, IPs]), - ok - end, - Clusters), - ok. - -string_of_ipaddr({IP, Port}) -> - lists:flatten(io_lib:format("~s:~p", [IP, Port])). - -choose_best_addr({cluster_by_addr, {IP,Port}}, _ClientAddr) -> - string_of_ipaddr({IP,Port}); -choose_best_addr({cluster_by_name, _}, ClientAddr) -> - string_of_ipaddr(ClientAddr). - -string_of_remote({cluster_by_addr, {IP,Port}}) -> - string_of_ipaddr({IP,Port}); -string_of_remote({cluster_by_name, ClusterName}) -> - ClusterName. - -%% Print info about this sink -%% Remote :: {ip,port} | ClusterName -showClusterConn({Remote,Pid}) -> - ConnName = string_of_remote(Remote), - PidStr = io_lib:format("~p", [Pid]), - %% try to get status from Pid of cluster control channel. - %% if we haven't connected successfully yet, it will time out, which we will fail - %% fast for since it's a local process, not a remote one. - try riak_core_cluster_conn:status(Pid, 2) of - {Pid, status, {ClientAddr, _Transport, Name, Members}} -> - IPs = [string_of_ipaddr(Addr) || Addr <- Members], - CAddr = choose_best_addr(Remote, ClientAddr), - io:format("~-20s ~-20s ~-15s ~p (via ~s)~n", - [ConnName, Name, PidStr,IPs, CAddr]); - {_StateName, SRemote} -> - io:format("~-20s ~-20s ~-15s (connecting to ~p)~n", - [ConnName, "", PidStr, string_of_remote(SRemote)]) - catch - 'EXIT':{timeout, _} -> - io:format("~-20s ~-20s ~-15s (status timed out)~n", - [ConnName, "", PidStr]) - end. - -connections([]) -> - %% get cluster manager's outbound connections to other "remote" clusters, - %% which for now, are all the "sinks". - {ok, Conns} = riak_core_cluster_mgr:get_connections(), - io:format("~-20s ~-20s ~-15s [Members]~n", ["Connection", "Cluster Name", ""]), - io:format("~-20s ~-20s ~-15s ---------~n", ["----------", "------------", "----------"]), - _ = [showClusterConn(Conn) || Conn <- Conns], - ok. - -connect([Address]) -> - ?LOG_USER_CMD("Connect to cluster at ~p", [Address]), - NWords = string:words(Address, $:), - case NWords of - 2 -> - IP = string:sub_word(Address, 1, $:), - PortStr = string:sub_word(Address, 2, $:), - connect([IP, PortStr]); - _ -> - io:format("Error: remote connection is missing port. Expected 'connect '~n"), - {error, {badarg, Address}} - end; -connect([IP, PortStr]) -> - ?LOG_USER_CMD("Connect to cluster at ~p:~p", [IP, PortStr]), - {Port,_Rest} = string:to_integer(PortStr), - case riak_core_connection:symbolic_clustername() of - "undefined" -> - io:format("Error: Unable to establish connections until local cluster is named.~n"), - io:format("First use 'riak-repl clustername '~n"), - {error, undefined_cluster_name}; - _Name -> - riak_core_cluster_mgr:add_remote_cluster({IP, Port}), - ok - end. +%% clustername([]) -> +%% MyName = riak_core_connection:symbolic_clustername(), +%% io:format("~s~n", [MyName]), +%% ok; +%% clustername([ClusterName]) -> +%% ?LOG_USER_CMD("Set clustername to ~p", [ClusterName]), +%% riak_core_ring_manager:ring_trans(fun riak_core_connection:set_symbolic_clustername/2, +%% ClusterName), +%% ok. + +%% connect([Address]) -> +%% ?LOG_USER_CMD("Connect to cluster at ~p", [Address]), +%% NWords = string:words(Address, $:), +%% case NWords of +%% 2 -> +%% IP = string:sub_word(Address, 1, $:), +%% PortStr = string:sub_word(Address, 2, $:), +%% connect([IP, PortStr]); +%% _ -> +%% io:format("Error: remote connection is missing port. Expected 'connect '~n"), +%% {error, {badarg, Address}} +%% end; +%% connect([IP, PortStr]) -> +%% ?LOG_USER_CMD("Connect to cluster at ~p:~p", [IP, PortStr]), +%% {Port,_Rest} = string:to_integer(PortStr), +%% case riak_core_connection:symbolic_clustername() of +%% "undefined" -> +%% io:format("Error: Unable to establish connections until local cluster is named.~n"), +%% io:format("First use 'riak-repl clustername '~n"), +%% {error, undefined_cluster_name}; +%% _Name -> +%% riak_core_cluster_mgr:add_remote_cluster({IP, Port}), +%% ok +%% end. %% remove a remote connection by clustername or by IP/Port address: %% clustername %% | ip:port %% | ip port -disconnect([Address]) -> - ?LOG_USER_CMD("Disconnect from cluster at ~p", [Address]), - NWords = string:words(Address, $:), - case NWords of - 1 -> - Remote = Address, - %% TODO: need to wrap a single ring transition around all of these. - %% fullsync(["stop", Remote]), - %% fullsync(["disable", Remote]), - %% realtime(["stop", Remote]), - %% realtime(["disable", Remote]), - %% tear down cluster manager connection - riak_core_cluster_mgr:remove_remote_cluster(Remote), - ok; - 2 -> - IP = string:sub_word(Address, 1, $:), - PortStr = string:sub_word(Address, 2, $:), - _ = disconnect([IP, PortStr]), - ok; - _ -> - {error, {badarg, Address}} - end; -disconnect([IP, PortStr]) -> - ?LOG_USER_CMD("Disconnect from cluster at ~p:~p", [IP, PortStr]), - {Port,_Rest} = string:to_integer(PortStr), - riak_core_cluster_mgr:remove_remote_cluster({IP, Port}), - ok. - -realtime([Cmd, Remote]) -> - case Cmd of - "enable" -> - ?LOG_USER_CMD("Enable Realtime Replication to cluster ~p", [Remote]), - riak_repl2_rt:enable(Remote); - "disable" -> - ?LOG_USER_CMD("Disable Realtime Replication to cluster ~p", [Remote]), - riak_repl2_rt:disable(Remote); - "start" -> - ?LOG_USER_CMD("Start Realtime Replication to cluster ~p", [Remote]), - riak_repl2_rt:start(Remote); - "stop" -> - ?LOG_USER_CMD("Stop Realtime Replication to cluster ~p", [Remote]), - riak_repl2_rt:stop(Remote) - end, - ok; -realtime([Cmd]) -> - Remotes = riak_repl2_rt:enabled(), - _ = case Cmd of - "start" -> - ?LOG_USER_CMD("Start Realtime Replication to all connected clusters", - []), - _ = [riak_repl2_rt:start(Remote) || Remote <- Remotes]; - "stop" -> - ?LOG_USER_CMD("Stop Realtime Replication to all connected clusters", - []), - _ = [riak_repl2_rt:stop(Remote) || Remote <- Remotes] - end, - ok. +%% disconnect([Address]) -> +%% ?LOG_USER_CMD("Disconnect from cluster at ~p", [Address]), +%% NWords = string:words(Address, $:), +%% case NWords of +%% 1 -> +%% Remote = Address, +%% %% TODO: need to wrap a single ring transition around all of these. +%% %% fullsync(["stop", Remote]), +%% %% fullsync(["disable", Remote]), +%% %% realtime(["stop", Remote]), +%% %% realtime(["disable", Remote]), +%% %% tear down cluster manager connection +%% riak_core_cluster_mgr:remove_remote_cluster(Remote), +%% ok; +%% 2 -> +%% IP = string:sub_word(Address, 1, $:), +%% PortStr = string:sub_word(Address, 2, $:), +%% _ = disconnect([IP, PortStr]), +%% ok; +%% _ -> +%% {error, {badarg, Address}} +%% end; +%% disconnect([IP, PortStr]) -> +%% ?LOG_USER_CMD("Disconnect from cluster at ~p:~p", [IP, PortStr]), +%% {Port,_Rest} = string:to_integer(PortStr), +%% riak_core_cluster_mgr:remove_remote_cluster({IP, Port}), +%% ok. + +%% realtime([Cmd, Remote]) -> +%% case Cmd of +%% "enable" -> +%% ?LOG_USER_CMD("Enable Realtime Replication to cluster ~p", [Remote]), +%% riak_repl2_rt:enable(Remote); +%% "disable" -> +%% ?LOG_USER_CMD("Disable Realtime Replication to cluster ~p", [Remote]), +%% riak_repl2_rt:disable(Remote); +%% "start" -> +%% ?LOG_USER_CMD("Start Realtime Replication to cluster ~p", [Remote]), +%% riak_repl2_rt:start(Remote); +%% "stop" -> +%% ?LOG_USER_CMD("Stop Realtime Replication to cluster ~p", [Remote]), +%% riak_repl2_rt:stop(Remote) +%% end, +%% ok; +%% realtime([Cmd]) -> +%% Remotes = riak_repl2_rt:enabled(), +%% _ = case Cmd of +%% "start" -> +%% ?LOG_USER_CMD("Start Realtime Replication to all connected clusters", +%% []), +%% _ = [riak_repl2_rt:start(Remote) || Remote <- Remotes]; +%% "stop" -> +%% ?LOG_USER_CMD("Stop Realtime Replication to all connected clusters", +%% []), +%% _ = [riak_repl2_rt:stop(Remote) || Remote <- Remotes] +%% end, +%% ok. fullsync([Cmd, Remote]) -> Leader = riak_core_cluster_mgr:get_leader(), @@ -819,23 +610,23 @@ modes(NewModes) -> set_modes(Modes), modes([]). -realtime_cascades(["enable"]) -> - ?LOG_USER_CMD("Enable Realtime Replication cascading", []), - riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, - always), - io:format("Realtime cascades enabled.~n"); -realtime_cascades(["disable"]) -> - ?LOG_USER_CMD("Disable Realtime Replication cascading", []), - riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, - never), - io:format("Realtime cascades disabled.~n"); -realtime_cascades(["show"]) -> - case app_helper:get_env(riak_repl, realtime_cascades, always) of - always -> - io:format("Realtime cascades are enabled.~n"); - never -> - io:format("Realtime cascades are disabled.~n") - end. +%% realtime_cascades(["enable"]) -> +%% ?LOG_USER_CMD("Enable Realtime Replication cascading", []), +%% riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, +%% always), +%% io:format("Realtime cascades enabled.~n"); +%% realtime_cascades(["disable"]) -> +%% ?LOG_USER_CMD("Disable Realtime Replication cascading", []), +%% riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, +%% never), +%% io:format("Realtime cascades disabled.~n"); +%% realtime_cascades(["show"]) -> +%% case app_helper:get_env(riak_repl, realtime_cascades, always) of +%% always -> +%% io:format("Realtime cascades are enabled.~n"); +%% never -> +%% io:format("Realtime cascades are disabled.~n") +%% end. cascades(Val) -> realtime_cascades(Val). @@ -1047,19 +838,6 @@ format_counter_stats([{_K,_IPAddr,_V}|T]) -> %% io:format("~p(~p): ~p~n", [K,IPAddr,V]), format_counter_stats(T). -make_listener(NodeName, IP, Port) -> - #repl_listener{nodename=list_to_atom(NodeName), - listen_addr={IP, list_to_integer(Port)}}. - -make_nat_listener(NodeName, IP, Port, PublicIP, PublicPort) -> - #nat_listener{nodename=list_to_atom(NodeName), - listen_addr={IP, list_to_integer(Port)}, - nat_addr={PublicIP, list_to_integer(PublicPort)}}. - - -make_site(SiteName, IP, Port) -> - #repl_site{name=SiteName, addrs=[{IP, list_to_integer(Port)}]}. - maybe_set_ring(_R, _R) -> ok; maybe_set_ring(_R1, R2) -> RC = riak_repl_ring:get_repl_config(R2), diff --git a/src/riak_repl_console12.erl b/src/riak_repl_console12.erl new file mode 100644 index 00000000..ab0a63aa --- /dev/null +++ b/src/riak_repl_console12.erl @@ -0,0 +1,206 @@ +%% @doc Console commands for "Version 2" replication, aka +%% 'mode_repl12'. +-module(riak_repl_console12). +-include("riak_repl.hrl"). + +%% Points where riak_repl_console calls in. +-export([register_usage/0, dispatch/1, commands_usage/0]). + +%% Commands +-export([add_listener/1, del_listener/1, add_nat_listener/1, + add_site/1, del_site/1, + start_fullsync/1, cancel_fullsync/1, + pause_fullsync/1, resume_fullsync/1]). + +-import(riak_repl_console, + [script_name/0, register_usage/2, get_ring/0, + maybe_set_ring/2]). + +-define(USAGE, + [{"add-listener", "add-listener "}, + {"add-nat-listener", "add-nat-listener "}, + {"del-listener", "del-listener "}, + {"add-site", "add-site "}, + {"del-site", "del-site "}, + {"start-fullsync", "start-fullsync"}, + {"cancel-fullsync", "cancel-fullsync"}, + {"pause-fullsync", "pause-fullsync"}, + {"resume-fullsync", "resume-fullsync"} + ]). + +%% @doc Attempts to dispatch a legacy command, returning `nomatch' if +%% the command did not match by name or number of arguments. If the +%% last thing on the line is `-h' or `--help', `nomatch' is returned +%% so that usage can be printed. +-spec dispatch([string()]) -> ok | error | nomatch. +dispatch(Cmd) -> + case lists:last(Cmd) of + "--help" -> nomatch; + "-h" -> nomatch; + _ -> dispatch_internal(Cmd) + end. + +dispatch_internal(["add-listener"|[_,_,_]=Params]) -> + add_listener(Params); +dispatch_internal(["add-nat-listener"|[_,_,_,_,_]=Params]) -> + add_nat_listener(Params); +dispatch_internal(["del-listener"|[_,_,_]=Params]) -> + del_listener(Params); +dispatch_internal(["add-site"|[_,_,_]=Params]) -> + add_site(Params); +dispatch_internal(["del-site"|[_]=Params]) -> + del_site(Params); +dispatch_internal(["start-fullsync"]) -> + start_fullsync([]); +dispatch_internal(["cancel-fullsync"]) -> + cancel_fullsync([]); +dispatch_internal(["pause-fullsync"]) -> + pause_fullsync([]); +dispatch_internal(["resume-fullsync"]) -> + resume_fullsync([]); +dispatch_internal(_) -> nomatch. + +%% @doc List of implemented commands for this module, for printing out +%% at the top-level command. +-spec commands_usage() -> string(). +commands_usage() -> + " Version 2 Commands:\n" + " add-listener Add a sink listener\n" + " add-nat-listener Add a sink listener with NAT\n" + " add-site Add a sink site\n" + " cancel-fullsync Cancel running fullsync replication\n" + " del-listener Delete a sink listener\n" + " del-site Delete a sink site\n" + " pause-fullsync Pause running fullsync replication\n" + " resume-fullsync Resume paused fullsync replication\n" + " start-fullsync Start fullsync replication". + +%% @doc Registers usage output with Clique. +register_usage() -> + _ = [ begin + register_usage([Cmd], [UsageStr, "\n\n", ?V2REPLDEP, "\n\n"]) + end || {Cmd, UsageStr} <- ?USAGE ], + ok. + +add_listener(Params) -> + warn_v2_repl(), + Ring = get_ring(), + case add_listener_internal(Ring,Params) of + {ok, NewRing} -> + ok = maybe_set_ring(Ring, NewRing); + error -> error + end. + +warn_v2_repl() -> + io:format(?V2REPLDEP++"~n~n", []), + lager:warning(?V2REPLDEP, []). + +add_nat_listener(Params) -> + lager:warning(?V2REPLDEP, []), + Ring = get_ring(), + case add_nat_listener_internal(Ring, Params) of + {ok, NewRing} -> + ok = maybe_set_ring(Ring, NewRing); + error -> error + end. + +add_listener_internal(Ring, [NodeName, IP, Port]) -> + Listener = make_listener(NodeName, IP, Port), + case lists:member(Listener#repl_listener.nodename, riak_core_ring:all_members(Ring)) of + true -> + case catch rpc:call(Listener#repl_listener.nodename, + riak_repl_util, valid_host_ip, [IP]) of + true -> + NewRing = riak_repl_ring:add_listener(Ring, Listener), + {ok,NewRing}; + false -> + io:format("~p is not a valid IP address for ~p\n", + [IP, Listener#repl_listener.nodename]), + error; + Error -> + io:format("Node ~p must be available to add listener: ~p\n", + [Listener#repl_listener.nodename, Error]), + error + end; + false -> + io:format("~p is not a member of the cluster\n", [Listener#repl_listener.nodename]), + error + end. + +add_nat_listener_internal(Ring, [NodeName, IP, Port, PublicIP, PublicPort]) -> + case add_listener_internal(Ring, [NodeName, IP, Port]) of + {ok,NewRing} -> + case inet_parse:address(PublicIP) of + {ok, _} -> + NatListener = make_nat_listener(NodeName, IP, Port, PublicIP, PublicPort), + NewRing2 = riak_repl_ring:add_nat_listener(NewRing, NatListener), + {ok, NewRing2}; + {error, IPParseError} -> + io:format("Invalid NAT IP address: ~p~n", [IPParseError]), + error + end; + error -> + io:format("Error adding nat address. ~n"), + error + end. + +del_listener([NodeName, IP, Port]) -> + lager:warning(?V2REPLDEP, []), + Ring = get_ring(), + Listener = make_listener(NodeName, IP, Port), + NewRing0 = riak_repl_ring:del_listener(Ring, Listener), + NewRing = riak_repl_ring:del_nat_listener(NewRing0, Listener), + ok = maybe_set_ring(Ring, NewRing). + +add_site([IP, Port, SiteName]) -> + lager:warning(?V2REPLDEP, []), + Ring = get_ring(), + Site = make_site(SiteName, IP, Port), + NewRing = riak_repl_ring:add_site(Ring, Site), + ok = maybe_set_ring(Ring, NewRing). + +del_site([SiteName]) -> + lager:warning(?V2REPLDEP, []), + Ring = get_ring(), + NewRing = riak_repl_ring:del_site(Ring, SiteName), + ok = maybe_set_ring(Ring, NewRing). + +start_fullsync([]) -> + lager:warning(?V2REPLDEP, []), + _ = [riak_repl_tcp_server:start_fullsync(Pid) || + Pid <- riak_repl_listener_sup:server_pids()], + io:format("Fullsync started~n"), + ok. + +cancel_fullsync([]) -> + lager:warning(?V2REPLDEP, []), + _ = [riak_repl_tcp_server:cancel_fullsync(Pid) || + Pid <- riak_repl_listener_sup:server_pids()], + io:format("Fullsync canceled~n"), + ok. + +pause_fullsync([]) -> + lager:warning(?V2REPLDEP, []), + _ = [riak_repl_tcp_server:pause_fullsync(Pid) || + Pid <- riak_repl_listener_sup:server_pids()], + io:format("Fullsync paused~n"), + ok. + +resume_fullsync([]) -> + lager:warning(?V2REPLDEP, []), + _ = [riak_repl_tcp_server:resume_fullsync(Pid) || + Pid <- riak_repl_listener_sup:server_pids()], + io:format("Fullsync resumed~n"), + ok. + +make_listener(NodeName, IP, Port) -> + #repl_listener{nodename=list_to_atom(NodeName), + listen_addr={IP, list_to_integer(Port)}}. + +make_nat_listener(NodeName, IP, Port, PublicIP, PublicPort) -> + #nat_listener{nodename=list_to_atom(NodeName), + listen_addr={IP, list_to_integer(Port)}, + nat_addr={PublicIP, list_to_integer(PublicPort)}}. + +make_site(SiteName, IP, Port) -> + #repl_site{name=SiteName, addrs=[{IP, list_to_integer(Port)}]}. diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl new file mode 100644 index 00000000..f25dc7dc --- /dev/null +++ b/src/riak_repl_console13.erl @@ -0,0 +1,622 @@ +%% @doc Console commands for "Version 3" replication, aka +%% 'mode_repl13'. +-module(riak_repl_console13). +-include("riak_repl.hrl"). +-export([register/0, commands_usage/0, upgrade/1]). + +-import(riak_repl_console, [register_command/4, script_name/0]). + +-import(clique_status, [text/1, alert/1, table/1]). + +-export([cluster_mgr_stats/0]). + +%%----------------------- +%% Interface +%%----------------------- + +-spec register() -> ok. +register() -> + ok = register_commands(), + ok = register_usage(), + ok. + +register_commands() -> + ok = register_command(["clusterstats"], [], + [{host, [{longname, "host"}, + {datatype, ip}]}, + {protocol, [{longname, "protocol"}, + {datatype, atom}]}], + fun clusterstats/2), + ok = register_command(["clustername"], [], + [{name, [{shortname, "n"}, + {longname, "name"}, + {datatype, string}]}], + fun clustername/2), + ok = register_command(["clusters"], [], [], fun clusters/2), + ok = register_command(["connections"], [], [], fun connections/2), + ok = register_command(["connect"], [{address, [{datatype, ip}]}], [], + fun connect/2), + ok = register_command(["disconnect"], [{remote, [{datatype, [ip, string]}]}], [], + fun disconnect/2), + ok = register_command(["realtime", "enable"], + [{remote, [{datatype, string}]}], + [], + fun realtime_enable/2), + ok = register_command(["realtime", "disable"], + [{remote, [{datatype, string}]}], + [], + fun realtime_disable/2), + ok = register_command(["realtime", "start"], + [{remote, [{datatype, string}]}], + [{all, [{longname, "all"}, + {shortname, "a"}]}], + fun realtime_start/2), + ok = register_command(["realtime", "stop"], + [{remote, [{datatype, string}]}], + [{all, [{longname, "all"}, + {shortname, "a"}]}], + fun realtime_stop/2), + ok = register_command(["realtime", "cascades", "enable"], + [],[], + fun realtime_cascades_enable/2), + ok = register_command(["realtime", "cascades", "disable"], + [],[], + fun realtime_cascades_disable/2), + ok = register_command(["realtime", "cascades", "show"], + [],[], + fun realtime_cascades_show/2), + ok = register_command(["fullsync", "enable"], + [{remote, [{datatype, string}]}], + [], + fun fullsync_enable/2), + ok = register_command(["fullsync", "disable"], + [{remote, [{datatype, string}]}], + [], + fun fullsync_disable/2), + ok = register_command(["fullsync", "start"], + [{remote, [{datatype, string}]}], + [{all, [{longname, "all"}, + {shortname, "a"}]}], + fun fullsync_start/2), + ok = register_command(["fullsync", "stop"], + [{remote, [{datatype, string}]}], + [{all, [{longname, "all"}, + {shortname, "a"}]}], + fun fullsync_stop/2). + + +register_usage(Cmd, Usage) -> + riak_repl_console:register_usage(Cmd, Usage). + +register_usage() -> + ok = register_usage(["clusterstats"], + "clusterstats [ --protocol=PROTO | --host=IP:PORT ]\n\n" + " Displays cluster statistics, optionally filtered by a protocol or host connection.\n\n" + " Options:\n" + " --protocol=PROTO Filters to a protocol where PROTO is one of:\n" + " rt_repl, proxy_get, identity\n" + " --host=IP:PORT Filters to a specific host, identified by IP and PORT"), + ok = register_usage(["clustername"], + "clustername [ (-n | --name) NAME ]\n\n" + " Shows or sets the symbolic clustername. Supplying the `-n` option sets the name.\n\n" + " Options:\n" + " -n NAME, --name NAME Sets the symbolic name to NAME"), + ok = register_usage(["clusters"], + "clusters\n\n" + " Displays information about known clusters."), + ok = register_usage(["connections"], + "connections\n\n" + " Displays a list of current replication connections."), + ok = register_usage(["connect"], + "connect address=IP:PORT\n\n" + " Connects to a remote cluster."), + ok = register_usage(["disconnect"], + "disconnect remote=(IP:PORT | NAME)\n\n" + " Disconnects from a connected remote cluster."), + ok = register_usage(["realtime"], realtime_usage()), + ok = register_usage(["realtime", "enable"], realtime_enable_disable_usage()), + ok = register_usage(["realtime", "disable"], realtime_enable_disable_usage()), + ok = register_usage(["realtime", "start"], realtime_start_stop_usage()), + ok = register_usage(["realtime", "stop"], realtime_start_stop_usage()), + ok = register_usage(["realtime", "cascades"], realtime_cascades_usage()), + ok = register_usage(["fullsync"], fullsync_usage()), + ok = register_usage(["fullsync", "enable"], fullsync_enable_disable_usage()), + ok = register_usage(["fullsync", "disable"], fullsync_enable_disable_usage()), + ok = register_usage(["fullsync", "start"], fullsync_start_stop_usage()), + ok = register_usage(["fullsync", "stop"], fullsync_start_stop_usage()). + + + +-spec commands_usage() -> string(). +commands_usage() -> + " Version 3 Commands:\n" + " clustername Show or set the cluster name\n" + " clusterstats Display cluster stats\n" + " clusters Display known clusters\n" + " connect Connect to a remote cluster\n" + " connections Display a list of connections\n" + " disconnect Disconnect from a remote cluster\n" + " fullsync Manipulate fullsync replication\n" + " nat-map Manipulate NAT mappings\n" + " proxy-get Manipulate proxy-get\n" + " realtime Manipulate realtime replication". + +realtime_usage() -> + "realtime [ ...]\n\n" + " Manipulate realtime replication. Realtime replication streams\n" + " incoming writes on the source cluster to the sink cluster(s).\n\n" + " Sub-commands:\n" + " enable Enable realtime replication\n" + " disable Disable realtime replication\n" + " start Start realtime replication\n" + " stop Stop realtime replication\n" + " cascades Manipulate cascading realtime replication". + +realtime_cascades_usage() -> + "realtime cascades SUBCOMMAND\n\n" + " Manipulate cascading realtime replication. When this cluster is a\n" + " sink and is receiving realtime replication, it can propagate\n" + " incoming writes to any clusters for which it is a source and\n" + " realtime replication is enabled.\n\n" + " Sub-commands:\n" + " enable Enable cascading realtime replication\n" + " disable Disable cascading realtime replication\n" + " show Show the current cascading realtime replication setting". + +realtime_enable_disable_usage() -> + "realtime ( enable | disable ) remote=CLUSTERNAME\n\n" + " Enable or disable realtime replication to CLUSTERNAME.". + +realtime_start_stop_usage() -> + "realtime ( start | stop ) ( remote=CLUSTERNAME | --all )\n\n" + " Start or stop realtime replication. When 'remote' is given, only\n" + " the specified sink CLUSTERNAME will be affected. When --all is given,\n" + " all realtime replication to all sinks will be started or stopped.". + +fullsync_usage() -> + "fullsync SUBCOMMAND ...\n\n" + " Manipulate fullsync replication. Fullsync replication compares data\n" + " on the source and the sink and then sends detected differences to\n" + " the sink cluster.\n\n" + " Sub-commands:\n" + " enable Enable fullsync replication\n" + " disable Disable fullsync replication\n" + " start Start fullsync replication\n" + " stop Stop fullsync replication\n". + +fullsync_enable_disable_usage() -> + "fullsync ( enable | disable ) remote=CLUSTERNAME\n\n" + " Enable or disable fullsync replication to CLUSTERNAME.". + +fullsync_start_stop_usage() -> + "fullsync ( start | stop ) ( remote=CLUSTERNAME | --all )\n\n" + " Start or stop fullsync replication. When 'remote' is given, only\n" + " the specified sink CLUSTERNAME will be affected. When --all is given,\n" + " all realtime replication to all sinks will be started or stopped.". + +upgrade(["clustername", [$-|_]|_]=Args) -> + %% Don't upgrade a call that includes a flag + Args; +upgrade(["clustername", Arg]=Args) -> + upgrade_warning(Args, "Use `clustername --name ~s`", [Arg]), + ["clustername", "-n", Arg]; +upgrade(["clusterstats", [$-|_]|_]=Args) -> + %% Don't upgrade a call that includes a flag + Args; +upgrade(["clusterstats", Arg]=Args) -> + case string:words(Arg, ":") of + 1 -> + upgrade_warning(Args, "Use `clusterstats --protocol ~s`", [Arg]), + ["clusterstats", "--protocol", Arg]; + 2 -> + upgrade_warning(Args, "Use `clusterstats --host ~s`", [Arg]), + ["clusterstats", "--host", Arg]; + _ -> Args + end; +upgrade(["connect", Arg|Rest]=Args) -> + case string:words(Arg, "=") of + 2 -> Args; + 1 -> + upgrade_warning(Args, "Use `connect address=~s`", [Arg]), + ["connect", "address="++Arg|Rest]; + _ -> Args + end; +upgrade(["disconnect", Arg|Rest]=Args) -> + case string:words(Arg, "=") of + 2 -> Args; + 1 -> + upgrade_warning(Args, "Use `disconnect remote=~s`", [Arg]), + ["disconnect", "remote="++Arg|Rest] + end; +upgrade(Args) -> + Args. + +%% @doc Registers a warning about using a deprecated form of a +%% command. +upgrade_warning(Args, Fmt, FArgs) -> + put(upgrade_warning, {string:join(Args, " "), io_lib:format(Fmt, FArgs)}). + +output(CmdOut) -> + case get(upgrade_warning) of + undefined -> CmdOut; + {Arguments, Message} -> + erase(upgrade_warning), + [error_msg("The command form `~s` is deprecated. ~s~n", [Arguments, Message]), + CmdOut] + end. + +error_msg(Fmt, Args) -> + [alert(text(io_lib:format(Fmt, Args)))]. + +%%----------------------- +%% Command: clusterstats +%%----------------------- +%% Show cluster stats for this node +clusterstats(_, Flags) -> + try + CMStats = cluster_mgr_stats(), + CConnStats = case Flags of + [] -> + riak_core_connection_mgr_stats:get_consolidated_stats(); + [{host, {IP, Port}}] when is_list(IP), is_integer(Port) -> + riak_core_connection_mgr_stats:get_stats_by_ip({IP,Port}); + [{protocol, ProtocolId}] when is_atom(ProtocolId) -> + riak_core_connection_mgr_stats:get_stats_by_protocol(ProtocolId); + _ -> + throw(badflags) + end, + %% TODO: make this output better + output(text(io_lib:format("~p~n", [CMStats ++ CConnStats]))) + catch + throw:badflags -> usage + end. + +%% rtq_stats() -> +%% case erlang:whereis(riak_repl2_rtq) of +%% Pid when is_pid(Pid) -> +%% [{realtime_queue_stats, riak_repl2_rtq:status()}]; +%% _ -> [] +%% end. + +cluster_mgr_stats() -> + case erlang:whereis(riak_repl_leader_gs) of + Pid when is_pid(Pid) -> + ConnectedClusters = case riak_core_cluster_mgr:get_known_clusters() of + {ok, Clusters} -> + [erlang:list_to_binary(Cluster) || Cluster <- + Clusters]; + Error -> Error + end, + [{cluster_name, + erlang:list_to_binary(riak_core_connection:symbolic_clustername())}, + {cluster_leader, riak_core_cluster_mgr:get_leader()}, + {connected_clusters, ConnectedClusters}]; + _ -> [] + end. + +%% clusterstats([Arg]) -> +%% NWords = string:words(Arg, $:), +%% case NWords of +%% 1 -> +%% %% assume protocol-id +%% ProtocolId = list_to_atom(Arg), +%% CConnStats = riak_core_connection_mgr_stats:get_stats_by_protocol(ProtocolId), +%% CMStats = cluster_mgr_stats(), +%% Stats = CMStats ++ CConnStats, +%% io:format("~p~n", [Stats]); +%% 2 -> +%% Address = Arg, +%% IP = string:sub_word(Address, 1, $:), +%% PortStr = string:sub_word(Address, 2, $:), +%% {Port,_Rest} = string:to_integer(PortStr), +%% CConnStats = riak_core_connection_mgr_stats:get_stats_by_ip({IP,Port}), +%% CMStats = cluster_mgr_stats(), +%% Stats = CMStats ++ CConnStats, +%% io:format("~p~n", [Stats]); +%% _ -> +%% {error, {badarg, Arg}} +%% end. + +%%----------------------- +%% Command: clustername +%%----------------------- +clustername([], []) -> + output(text(io_lib:format("Cluster name: ~s~n", [riak_core_connection:symbolic_clustername()]))); +clustername([], [{name, ClusterName}]) -> + riak_core_ring_manager:ring_trans(fun riak_core_connection:set_symbolic_clustername/2, + ClusterName), + output(text(io_lib:format("Cluster name was set to: ~s~n", [ClusterName]))). + +%%----------------------- +%% Command: clusters +%%----------------------- +clusters([],[]) -> + {ok, Clusters} = riak_core_cluster_mgr:get_known_clusters(), + output(text([ begin + {ok,Members} = riak_core_cluster_mgr:get_ipaddrs_of_cluster(ClusterName), + IPs = [string_of_ipaddr(Addr) || Addr <- Members], + io_lib:format("~s: ~p~n", [ClusterName, IPs]) + end || ClusterName <- Clusters])). + +%%----------------------- +%% Command: connections +%%----------------------- +connections([], []) -> + %% get cluster manager's outbound connections to other "remote" clusters, + %% which for now, are all the "sinks". + {ok, Conns} = riak_core_cluster_mgr:get_connections(), + Headers = [{connection, "Connection"}, + {cluster_name, "Cluster Name"}, + {pid, "Ctrl-Pid"}, + {members, "Members"}, + {status, "Status"}], + Rows = [format_cluster_conn(Conn) || Conn <- Conns], + output(table([Headers|Rows])). + +string_of_ipaddr({IP, Port}) -> + lists:flatten(io_lib:format("~s:~p", [IP, Port])). + +choose_best_addr({cluster_by_addr, {IP,Port}}, _ClientAddr) -> + string_of_ipaddr({IP,Port}); +choose_best_addr({cluster_by_name, _}, ClientAddr) -> + string_of_ipaddr(ClientAddr). + +string_of_remote({cluster_by_addr, {IP,Port}}) -> + string_of_ipaddr({IP,Port}); +string_of_remote({cluster_by_name, ClusterName}) -> + ClusterName. + +%% Format info about this sink into a clique table row. +%% Remote :: {ip,port} | ClusterName +format_cluster_conn({Remote,Pid}) -> + {ClusterName, MemberList, Status} = get_cluster_conn_status(Remote, Pid), + [{connection, string_of_remote(Remote)}, + {cluster_name, ClusterName}, + {pid, io_lib:format("~p", [Pid])}, + {members, format_cluster_conn_members(MemberList)}, + {status, format_cluster_conn_status(Status)}]. + +get_cluster_conn_status(Remote, Pid) -> + %% try to get status from Pid of cluster control channel. if we + %% haven't connected successfully yet, it will time out, which we + %% will fail fast for since it's a local process, not a remote + %% one. + try riak_core_cluster_conn:status(Pid, 2) of + {Pid, status, {ClientAddr, _Transport, Name, Members}} -> + CAddr = choose_best_addr(Remote, ClientAddr), + {Name, Members, {via, CAddr}}; + {_StateName, SRemote} -> + {"", [], {connecting, SRemote}} + catch + 'EXIT':{timeout, _} -> + {"", [], timeout} + end. + +format_cluster_conn_status({via, CAddr}) -> io_lib:format("via ~s", [CAddr]); +format_cluster_conn_status({connecting, SRemote}) -> io_lib:format("connecting to ~s", [string_of_remote(SRemote)]); +format_cluster_conn_status(timeout) -> "timed out". + +format_cluster_conn_members(Members) -> + string:join([ string_of_ipaddr(Addr) || Addr <- Members ], ","). + +%%----------------------- +%% Command: connect +%%----------------------- +connect([{address, {IP, Port}}], []) -> + ?LOG_USER_CMD("Connect to cluster at ~p:~p", [IP, Port]), + case riak_core_connection:symbolic_clustername() of + "undefined" -> + %% TODO: This should return an error, not a bare status, + %% but we still want to be able to print to stderr. This + %% will require a clique enhancement. + error_msg("Error: Unable to establish connections until local cluster is named.~n" + "First use ~s clustername --name NAME ~n", [script_name()]); + _Name -> + riak_core_cluster_mgr:add_remote_cluster({IP, Port}), + [text(io_lib:format("Connecting to remote cluster at ~p:~p.", [IP, Port]))] + end; +connect(_, _) -> + usage. + + +%%----------------------- +%% Command: disconnect +%%----------------------- +disconnect([{remote, {IP, Port}}], []) -> + ?LOG_USER_CMD("Disconnect from cluster at ~p:~p", [IP, Port]), + riak_core_cluster_mgr:remove_remote_cluster({IP, Port}), + [text(io_lib:format("Disconnecting from cluster at ~p:~p~n", [IP, Port]))]; +disconnect([{remote, Name}], []) -> + ?LOG_USER_CMD("Disconnect from cluster ~p", [Name]), + riak_core_cluster_mgr:remove_remote_cluster(Name), + [text(io_lib:format("Disconnecting from cluster ~p~n", [Name]))]; +disconnect(_, _) -> + usage. + + +%%-------------------------- +%% Command: realtime enable +%%-------------------------- + +realtime_enable([{remote, Remote}], []) -> + ?LOG_USER_CMD("Enable Realtime Replication to cluster ~p", [Remote]), + case riak_repl2_rt:enable(Remote) of + not_changed -> + [alert(text(io_lib:format("Realtime replication to cluster ~p already enabled!~n", [Remote])))]; + {ok, _} -> + [text(io_lib:format("Realtime replication to cluster ~p enabled.~n", [Remote]))] + end; +realtime_enable(_, _) -> + usage. + +%%-------------------------- +%% Command: realtime disable +%%-------------------------- +realtime_disable([{remote, Remote}], []) -> + ?LOG_USER_CMD("Disable Realtime Replication to cluster ~p", [Remote]), + case riak_repl2_rt:disable(Remote) of + not_changed -> + error_msg("Realtime replication to cluster ~p already disabled!~n", [Remote]); + {ok, _} -> + [text(io_lib:format("Realtime replication to cluster ~p disabled.~n", [Remote]))] + end; +realtime_disable(_, _) -> + usage. + +%%-------------------------- +%% Command: realtime start +%%-------------------------- +realtime_start([{remote, Remote}], []) -> + ?LOG_USER_CMD("Start Realtime Replication to cluster ~p", [Remote]), + case riak_repl2_rt:start(Remote) of + not_changed -> + error_msg("Realtime replication to cluster ~p is already started or not enabled!~n", [Remote]); + {ok, _} -> + [text(io_lib:format("Realtime replication to cluster ~p started.~n", [Remote]))] + end; +realtime_start([], [{all, _}]) -> + ?LOG_USER_CMD("Start Realtime Replication to all connected clusters", []), + Remotes = riak_repl2_rt:enabled(), + [ realtime_start([{remote, Remote}], []) || Remote <- Remotes ]; +realtime_start(_, _) -> + usage. + +%%-------------------------- +%% Command: realtime stop +%%-------------------------- +realtime_stop([{remote, Remote}], []) -> + ?LOG_USER_CMD("Stop Realtime Replication to cluster ~p", [Remote]), + case riak_repl2_rt:stop(Remote) of + not_changed -> + error_msg("Realtime replication to cluster ~p is already stopped or not enabled!~n", [Remote]); + {ok, _} -> + [text(io_lib:format("Realtime replication to cluster ~p stopped.~n", [Remote]))] + end; +realtime_stop([], [{all, _}]) -> + ?LOG_USER_CMD("Stop Realtime Replication to all connected clusters", []), + Remotes = riak_repl2_rt:enabled(), + [ realtime_stop([{remote, Remote}], []) || Remote <- Remotes ]; +realtime_stop(_, _) -> + usage. + +%%-------------------------- +%% Command: realtime cascades enable +%%-------------------------- +realtime_cascades_enable([], []) -> + ?LOG_USER_CMD("Enable Realtime Replication cascading", []), + riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, + always), + text(io_lib:format("Realtime cascades enabled.~n")); +realtime_cascades_enable(_,_) -> + usage. + +%%-------------------------- +%% Command: realtime cascades disable +%%-------------------------- + +realtime_cascades_disable([], []) -> + ?LOG_USER_CMD("Disable Realtime Replication cascading", []), + riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, + never), + text(io_lib:format("Realtime cascades disabled.~n")); +realtime_cascades_disable(_,_) -> + usage. + +%%-------------------------- +%% Command: realtime cascades show +%%-------------------------- + +realtime_cascades_show([], []) -> + case app_helper:get_env(riak_repl, realtime_cascades, always) of + always -> + text(io_lib:format("Realtime cascades are enabled.~n")); + never -> + text(io_lib:format("Realtime cascades are disabled.~n")) + end; +realtime_cascades_show(_, _) -> + usage. + + +%%-------------------------- +%% Command: fullsync enable +%%-------------------------- + +fullsync_enable([{remote, Remote}], []) -> + Leader = riak_core_cluster_mgr:get_leader(), + ?LOG_USER_CMD("Enable Fullsync Replication to cluster ~p", [Remote]), + riak_core_ring_manager:ring_trans(fun + riak_repl_ring:fs_enable_trans/2, Remote), + _ = riak_repl2_fscoordinator_sup:start_coord(Leader, Remote), + text(io_lib:format("Fullsync replication to cluster ~p enabled.", [Remote])); +fullsync_enable(_, _) -> + usage. + +%%-------------------------- +%% Command: fullsync disable +%%-------------------------- + +fullsync_disable([{remote, Remote}], []) -> + Leader = riak_core_cluster_mgr:get_leader(), + ?LOG_USER_CMD("Disable Fullsync Replication to cluster ~p", [Remote]), + riak_core_ring_manager:ring_trans(fun + riak_repl_ring:fs_disable_trans/2, Remote), + _ = riak_repl2_fscoordinator_sup:stop_coord(Leader, Remote), + text(io_lib:format("Fullsync replication to cluster ~p disabled.", [Remote])); +fullsync_disable(_, _) -> + usage. + + +%%-------------------------- +%% Command: fullsync start +%%-------------------------- + +fullsync_start([{remote, Remote}], []) -> + Leader = riak_core_cluster_mgr:get_leader(), + ?LOG_USER_CMD("Start Fullsync Replication to cluster ~p", [Remote]), + Fullsyncs = riak_repl2_fscoordinator_sup:started(Leader), + case proplists:get_value(Remote, Fullsyncs) of + undefined -> + %% io:format("Fullsync not enabled for cluster ~p~n", [Remote]), + %% io:format("Use 'fullsync enable ~p' before start~n", [Remote]), + %% {error, not_enabled}; + error_msg("Fullsync not enabled for cluster ~p~n" + "Use 'fullsync enable ~p' before start~n", [Remote, Remote]); + Pid -> + riak_repl2_fscoordinator:start_fullsync(Pid), + text(io_lib:format("Fullsync replication to cluster ~p started.", [Remote])) + end; +fullsync_start([], [{all,_}]) -> + Leader = riak_core_cluster_mgr:get_leader(), + Fullsyncs = riak_repl2_fscoordinator_sup:started(Leader), + ?LOG_USER_CMD("Start Fullsync Replication to all connected clusters",[]), + _ = [riak_repl2_fscoordinator:start_fullsync(Pid) || {_, Pid} <- + Fullsyncs], + text("Fullsync replication started to all connected clusters."); +fullsync_start(_, _) -> + usage. + +%%-------------------------- +%% Command: fullsync stop +%%-------------------------- + +fullsync_stop([{remote, Remote}], []) -> + Leader = riak_core_cluster_mgr:get_leader(), + ?LOG_USER_CMD("Stop Fullsync Replication to cluster ~p", [Remote]), + Fullsyncs = riak_repl2_fscoordinator_sup:started(Leader), + case proplists:get_value(Remote, Fullsyncs) of + undefined -> + %% Fullsync is not enabled, but carry on quietly. + error_msg("Fullsync is not enabled for cluster ~p.", [Remote]); + Pid -> + riak_repl2_fscoordinator:stop_fullsync(Pid), + text(io_lib:format("Fullsync stopped for cluster ~p.", [Remote])) + end; +fullsync_stop([], [{all,_}]) -> + Leader = riak_core_cluster_mgr:get_leader(), + Fullsyncs = riak_repl2_fscoordinator_sup:started(Leader), + ?LOG_USER_CMD("Stop Fullsync Replication to all connected clusters",[]), + _ = [riak_repl2_fscoordinator:stop_fullsync(Pid) || {_, Pid} <- + Fullsyncs], + text("Fullsync replication stopped to all connected clusters."); +fullsync_stop(_, _) -> + usage. From eb2e6435a499fa4005e919e3bea766cafedee8a6 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Wed, 18 Feb 2015 18:01:27 -0600 Subject: [PATCH 20/37] WIP Add proxy-get commands and usage output --- src/riak_repl_console.erl | 56 +++++----- src/riak_repl_console13.erl | 203 +++++++++++++++++++++++++++++++++++- 2 files changed, 228 insertions(+), 31 deletions(-) diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index 7328459f..bf9a305c 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -22,14 +22,14 @@ max_fssource_cluster/1, max_fssink_node/1, %% realtime_cascades/1, - cascades/1, + %% cascades/1, show_nat_map/1, add_nat_map/1, - del_nat_map/1, - add_block_provider_redirect/1, - show_block_provider_redirect/1, - show_local_cluster_id/1, - delete_block_provider_redirect/1 + del_nat_map/1 %, + %% add_block_provider_redirect/1, + %% show_block_provider_redirect/1, + %% show_local_cluster_id/1, + %% delete_block_provider_redirect/1 ]). -export([get_ring/0, maybe_set_ring/2]). @@ -628,8 +628,8 @@ modes(NewModes) -> %% io:format("Realtime cascades are disabled.~n") %% end. -cascades(Val) -> - realtime_cascades(Val). +%% cascades(Val) -> +%% realtime_cascades(Val). %% For each of these "max" parameter changes, we need to make an rpc multi-call to every node %% so that all nodes have the new value in their application environment. That way, whoever @@ -725,28 +725,28 @@ del_nat_map([External, Internal]) -> %% of this writing we had no reliable way to map a clustername to an id %% over disterlang. When this API becomes available, this feature may use %% it. -add_block_provider_redirect([FromClusterId, ToClusterId]) -> - lager:info("Redirecting cluster id: ~p to ~p", [FromClusterId, ToClusterId]), - riak_core_metadata:put({<<"replication">>, <<"cluster-mapping">>}, - FromClusterId, ToClusterId). - -show_block_provider_redirect([FromClusterId]) -> - case riak_core_metadata:get({<<"replication">>, <<"cluster-mapping">>}, FromClusterId) of - undefined -> - io:format("No mapping for ~p~n", [FromClusterId]); - ToClusterId -> - io:format("Cluster id ~p redirecting to cluster id ~p~n", [FromClusterId, ToClusterId]) - end. +%% add_block_provider_redirect([FromClusterId, ToClusterId]) -> +%% lager:info("Redirecting cluster id: ~p to ~p", [FromClusterId, ToClusterId]), +%% riak_core_metadata:put({<<"replication">>, <<"cluster-mapping">>}, +%% FromClusterId, ToClusterId). + +%% show_block_provider_redirect([FromClusterId]) -> +%% case riak_core_metadata:get({<<"replication">>, <<"cluster-mapping">>}, FromClusterId) of +%% undefined -> +%% io:format("No mapping for ~p~n", [FromClusterId]); +%% ToClusterId -> +%% io:format("Cluster id ~p redirecting to cluster id ~p~n", [FromClusterId, ToClusterId]) +%% end. -delete_block_provider_redirect([FromClusterId]) -> - lager:info("Deleting redirect to ~p", [FromClusterId]), - riak_core_metadata:delete({<<"replication">>, <<"cluster-mapping">>}, FromClusterId). +%% delete_block_provider_redirect([FromClusterId]) -> +%% lager:info("Deleting redirect to ~p", [FromClusterId]), +%% riak_core_metadata:delete({<<"replication">>, <<"cluster-mapping">>}, FromClusterId). -show_local_cluster_id([]) -> - {ok, Ring} = riak_core_ring_manager:get_my_ring(), - ClusterId = lists:flatten( - io_lib:format("~p", [riak_core_ring:cluster_name(Ring)])), - io:format("local cluster id: ~p~n", [ClusterId]). +%% show_local_cluster_id([]) -> +%% {ok, Ring} = riak_core_ring_manager:get_my_ring(), +%% ClusterId = lists:flatten( +%% io_lib:format("~p", [riak_core_ring:cluster_name(Ring)])), +%% io:format("local cluster id: ~p~n", [ClusterId]). %% helper functions diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index f25dc7dc..9726521e 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -18,6 +18,8 @@ register() -> ok = register_commands(), ok = register_usage(), + %% TODO: add max_fs* settings + %% ok = register_configs(), ok. register_commands() -> @@ -82,7 +84,28 @@ register_commands() -> [{remote, [{datatype, string}]}], [{all, [{longname, "all"}, {shortname, "a"}]}], - fun fullsync_stop/2). + fun fullsync_stop/2), + ok = register_command(["proxy-get", "enable"], + [{remote, [{datatype, string}]}], + [], fun proxy_get_enable/2), + ok = register_command(["proxy-get", "disable"], + [{remote, [{datatype, string}]}], + [], fun proxy_get_disable/2), + ok = register_command(["proxy-get", "redirect", "cluster-id"], + [], + [], fun proxy_get_redirect_cluster_id/2), + ok = register_command(["proxy-get", "redirect", "show"], + [{from, [{datatype, string}]}], + [], fun proxy_get_redirect_show/2), + ok = register_command(["proxy-get", "redirect", "add"], + [{from, [{datatype, string}]}, + {to, [{datatype, string}]}], + [], fun proxy_get_redirect_add/2), + ok = register_command(["proxy-get", "redirect", "delete"], + [{from, [{datatype, string}]}, + {to, [{datatype, string}]}], + [], fun proxy_get_redirect_delete/2), + ok. register_usage(Cmd, Usage) -> @@ -123,8 +146,15 @@ register_usage() -> ok = register_usage(["fullsync", "enable"], fullsync_enable_disable_usage()), ok = register_usage(["fullsync", "disable"], fullsync_enable_disable_usage()), ok = register_usage(["fullsync", "start"], fullsync_start_stop_usage()), - ok = register_usage(["fullsync", "stop"], fullsync_start_stop_usage()). - + ok = register_usage(["fullsync", "stop"], fullsync_start_stop_usage()), + ok = register_usage(["proxy-get"], proxy_get_usage()), + ok = register_usage(["proxy-get", "enable"], proxy_get_enable_disable_usage()), + ok = register_usage(["proxy-get", "disable"], proxy_get_usage()), + ok = register_usage(["proxy-get", "redirect"], proxy_get_usage()), + ok = register_usage(["proxy-get", "redirect", "show"], fun proxy_get_redirect_show_usage/0), + ok = register_usage(["proxy-get", "redirect", "add"], fun proxy_get_redirect_add_delete_usage/0), + ok = register_usage(["proxy-get", "redirect", "delete"], fun proxy_get_redirect_add_delete_usage/0), + ok = register_usage(["proxy-get", "redirect", "cluster-id"], proxy_get_redirect_usage()). -spec commands_usage() -> string(). @@ -194,6 +224,44 @@ fullsync_start_stop_usage() -> " the specified sink CLUSTERNAME will be affected. When --all is given,\n" " all realtime replication to all sinks will be started or stopped.". +proxy_get_usage() -> + "proxy-get SUBCOMMAND ...\n\n" + " Manipulate proxy-get functionality. Proxy-get allows sink clusters\n" + " to actively fetch remote objects over a realtime replication\n" + " connection. Currently, this is only used by Riak CS.\n\n" + " Sub-commands:\n" + " enable Enable proxy-get on the source\n" + " disable Disable proxy-get on the source\n" + " redirect Manipulation proxy-get redirection". + +proxy_get_enable_disable_usage() -> + "proxy-get ( enable | disable ) remote=CLUSTERNAME\n\n" + " Enables or disables proxy-get requests from sink CLUSTERNAME to this\n" + " source cluster.". + +proxy_get_redirect_usage() -> + "proxy-get redirect SUBCOMMAND ...\n\n" + " Manipulate proxy-get redirection functionality. Redirection allows\n" + " existing proxy-get connections to be redirected to new source\n" + " clusters so that the original source cluster can be decommissioned.\n\n" + " Sub-commands:\n" + " add Add a proxy-get redirection\n" + " delete Delete an existing proxy-get redirection\n" + " show Show a proxy-get redirection\n" + " cluster-id Display the local cluster's identifier". + +proxy_get_redirect_show_usage() -> + "proxy-get redirect show from=SOURCE\n\n" + " Show an existing proxy-get redirection. SOURCE must correspond to\n" + " the result from the `" ++ script_name() ++ "proxy-get redirect cluster-id` command.". + +proxy_get_redirect_add_delete_usage() -> + "proxy-get redirect ( add | delete ) from=SOURCE to=DESTINATION\n\n" + " Add or delete a proxy-get redirection. Arguments SOURCE and\n" + " DESTINATION must correspond to the result from the `" ++ script_name() ++ "\n" + " proxy-get redirect cluster-id` command.". + + upgrade(["clustername", [$-|_]|_]=Args) -> %% Don't upgrade a call that includes a flag Args; @@ -228,6 +296,52 @@ upgrade(["disconnect", Arg|Rest]=Args) -> upgrade_warning(Args, "Use `disconnect remote=~s`", [Arg]), ["disconnect", "remote="++Arg|Rest] end; +upgrade(["realtime", Command, Arg|Rest]=Args) when Command == "enable"; + Command == "disable"; + Command == "start"; + Command == "stop" -> + case string:words(Arg, "=") of + 2 -> Args; + 1 -> + upgrade_warning(Args, "Use `realtime ~s remote=~s`", [Command, Arg]), + ["realtime", Command, "remote="++Arg|Rest] + end; +upgrade(["realtime", Command]=Args) when Command == "start"; + Command == "stop" -> + upgrade_warning(Args, "Use `realtime ~s --all`", [Command]), + ["realtime", Command, "--all"]; +upgrade(["realtime", "cascades", "always"]=Args) -> + upgrade_warning(Args, "Use `realtime cascades enable`", []), + ["realtime", "cascades", "enable"]; +upgrade(["realtime", "cascades", "never"]=Args) -> + upgrade_warning(Args, "Use `realtime cascades disable`", []), + ["realtime", "cascades", "disable"]; +upgrade(["fullsync", Command, Arg|Rest]=Args) when Command == "enable"; + Command == "disable"; + Command == "start"; + Command == "stop" -> + case string:words(Arg, "=") of + 2 -> Args; + 1 -> + upgrade_warning(Args, "Use `fullsync ~s remote=~s`", [Command, Arg]), + ["fullsync", Command, "remote="++Arg|Rest] + end; +upgrade(["fullsync", Command]=Args) when Command == "start"; + Command == "stop" -> + upgrade_warning(Args, "Use `fullsync ~s --all`", [Command]), + ["fullsync", Command, "--all"]; +upgrade(["fullsync", Key]=Args) when Key == "max_fssource_node"; + Key == "max_fssource_cluster"; + Key == "max_fssink_node" -> + TKey = config_key_translation(Key), + upgrade_warning(Args, "Use `show ~s`", [TKey]), + ["show", TKey]; +upgrade(["fullsync", Key, Value]=Args) when Key == "max_fssource_node"; + Key == "max_fssource_cluster"; + Key == "max_fssink_node" -> + TKey = config_key_translation(Key), + upgrade_warning(Args, "Use `show ~s`", [TKey]), + ["set", TKey++"="++Value]; upgrade(Args) -> Args. @@ -236,6 +350,11 @@ upgrade(Args) -> upgrade_warning(Args, Fmt, FArgs) -> put(upgrade_warning, {string:join(Args, " "), io_lib:format(Fmt, FArgs)}). +config_key_translation("max_fssource_node") -> "mdc.fullsync.source.max_workers_per_node"; +config_key_translation("max_fssource_cluster") -> "mdc.fullsync.source.max_workers_per_cluster"; +config_key_translation("max_fssink_node") -> "mdc.fullsync.sink.max_workers_per_node". + + output(CmdOut) -> case get(upgrade_warning) of undefined -> CmdOut; @@ -620,3 +739,81 @@ fullsync_stop([], [{all,_}]) -> text("Fullsync replication stopped to all connected clusters."); fullsync_stop(_, _) -> usage. + +%%-------------------------- +%% Command: proxy-get enable +%%-------------------------- + +proxy_get_enable([{remote, Remote}], []) -> + ?LOG_USER_CMD("Enable Riak CS Proxy GET block provider for ~p",[Remote]), + riak_core_ring_manager:ring_trans(fun + riak_repl_ring:pg_enable_trans/2, Remote), + text(io_lib:format("Proxy-get to cluster ~s has been enabled.", [Remote])); +proxy_get_enable(_, _) -> + usage. + + +%%-------------------------- +%% Command: proxy-get disable +%%-------------------------- + +proxy_get_disable([{remote, Remote}], []) -> + ?LOG_USER_CMD("Disable Riak CS Proxy GET block provider for ~p",[Remote]), + riak_core_ring_manager:ring_trans(fun + riak_repl_ring:pg_disable_trans/2, Remote), + text(io_lib:format("Proxy-get to cluster ~s has been disabled.", [Remote])); +proxy_get_disable(_, _) -> + usage. + +%%-------------------------- +%% Command: proxy-get redirect cluster-id +%%-------------------------- + +proxy_get_redirect_cluster_id([], []) -> + {ok, Ring} = riak_core_ring_manager:get_my_ring(), + ClusterId = lists:flatten( + io_lib:format("~p", [riak_core_ring:cluster_name(Ring)])), + text(io:format("local cluster id: ~p~n", [ClusterId])); +proxy_get_redirect_cluster_id(_, _) -> + usage. + +%%-------------------------- +%% Command: proxy-get redirect show +%%-------------------------- + +proxy_get_redirect_show([{from, FromClusterId}], []) -> + case riak_core_metadata:get({<<"replication">>, <<"cluster-mapping">>}, FromClusterId) of + undefined -> + text(io_lib:format("No mapping for ~p~n", [FromClusterId])); + ToClusterId -> + text(io_lib:format("Cluster id ~p redirecting to cluster id ~p~n", [FromClusterId, ToClusterId])) + end; +proxy_get_redirect_show(_, _) -> + usage. + +%%-------------------------- +%% Command: proxy-get redirect add +%%-------------------------- + +proxy_get_redirect_add([{to, _}=To, {from, _}=From], []) -> + proxy_get_redirect_add([From, To], []); +proxy_get_redirect_add([{from, FromClusterId}, {to, ToClusterId}], []) -> + lager:info("Redirecting cluster id: ~p to ~p", [FromClusterId, ToClusterId]), + riak_core_metadata:put({<<"replication">>, <<"cluster-mapping">>}, + FromClusterId, ToClusterId), + text(io_lib:format("Redirected proxy-get from cluster ~s to cluster ~s~n", + [FromClusterId, ToClusterId])); +proxy_get_redirect_add(_, _) -> + usage. + + +%%-------------------------- +%% Command: proxy-get redirect delete +%%-------------------------- + +proxy_get_redirect_delete([{from, FromClusterId}], []) -> + lager:info("Deleting redirect to ~p", [FromClusterId]), + riak_core_metadata:delete({<<"replication">>, <<"cluster-mapping">>}, FromClusterId), + text(io_lib:format("Deleted proxy-get redirect from cluster ~s~n", [FromClusterId])); +proxy_get_redirect_delete(_, _) -> + usage. From c0687548b4f31d6d1ae2411e9cc4baed6ae0cd64 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Thu, 19 Feb 2015 10:50:47 -0600 Subject: [PATCH 21/37] WIP Refactor ubiquitous text+io_lib into a local function --- src/riak_repl_console13.erl | 64 ++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index 9726521e..a2f60d94 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -365,7 +365,13 @@ output(CmdOut) -> end. error_msg(Fmt, Args) -> - [alert(text(io_lib:format(Fmt, Args)))]. + [alert(text_msg(Fmt, Args))]. + +text_msg(Str) -> + text_msg(Str, []). + +text_msg(Fmt, Args) -> + text(io_lib:format(Fmt, Args)). %%----------------------- %% Command: clusterstats @@ -385,7 +391,7 @@ clusterstats(_, Flags) -> throw(badflags) end, %% TODO: make this output better - output(text(io_lib:format("~p~n", [CMStats ++ CConnStats]))) + output(text_msg("~p~n", [CMStats ++ CConnStats])) catch throw:badflags -> usage end. @@ -440,11 +446,11 @@ cluster_mgr_stats() -> %% Command: clustername %%----------------------- clustername([], []) -> - output(text(io_lib:format("Cluster name: ~s~n", [riak_core_connection:symbolic_clustername()]))); + output(text_msg("Cluster name: ~s~n", [riak_core_connection:symbolic_clustername()])); clustername([], [{name, ClusterName}]) -> riak_core_ring_manager:ring_trans(fun riak_core_connection:set_symbolic_clustername/2, ClusterName), - output(text(io_lib:format("Cluster name was set to: ~s~n", [ClusterName]))). + output(text_msg("Cluster name was set to: ~s~n", [ClusterName])). %%----------------------- %% Command: clusters @@ -529,10 +535,10 @@ connect([{address, {IP, Port}}], []) -> %% but we still want to be able to print to stderr. This %% will require a clique enhancement. error_msg("Error: Unable to establish connections until local cluster is named.~n" - "First use ~s clustername --name NAME ~n", [script_name()]); + "First use ~s clustername --name NAME ~n", [script_name()]); _Name -> riak_core_cluster_mgr:add_remote_cluster({IP, Port}), - [text(io_lib:format("Connecting to remote cluster at ~p:~p.", [IP, Port]))] + [text_msg("Connecting to remote cluster at ~p:~p.", [IP, Port])] end; connect(_, _) -> usage. @@ -544,11 +550,11 @@ connect(_, _) -> disconnect([{remote, {IP, Port}}], []) -> ?LOG_USER_CMD("Disconnect from cluster at ~p:~p", [IP, Port]), riak_core_cluster_mgr:remove_remote_cluster({IP, Port}), - [text(io_lib:format("Disconnecting from cluster at ~p:~p~n", [IP, Port]))]; + [text_msg("Disconnecting from cluster at ~p:~p~n", [IP, Port])]; disconnect([{remote, Name}], []) -> ?LOG_USER_CMD("Disconnect from cluster ~p", [Name]), riak_core_cluster_mgr:remove_remote_cluster(Name), - [text(io_lib:format("Disconnecting from cluster ~p~n", [Name]))]; + [text_msg("Disconnecting from cluster ~p~n", [Name])]; disconnect(_, _) -> usage. @@ -561,9 +567,9 @@ realtime_enable([{remote, Remote}], []) -> ?LOG_USER_CMD("Enable Realtime Replication to cluster ~p", [Remote]), case riak_repl2_rt:enable(Remote) of not_changed -> - [alert(text(io_lib:format("Realtime replication to cluster ~p already enabled!~n", [Remote])))]; + [error_msg("Realtime replication to cluster ~p already enabled!~n", [Remote])]; {ok, _} -> - [text(io_lib:format("Realtime replication to cluster ~p enabled.~n", [Remote]))] + [text_msg("Realtime replication to cluster ~p enabled.~n", [Remote])] end; realtime_enable(_, _) -> usage. @@ -577,7 +583,7 @@ realtime_disable([{remote, Remote}], []) -> not_changed -> error_msg("Realtime replication to cluster ~p already disabled!~n", [Remote]); {ok, _} -> - [text(io_lib:format("Realtime replication to cluster ~p disabled.~n", [Remote]))] + [text_msg("Realtime replication to cluster ~p disabled.~n", [Remote])] end; realtime_disable(_, _) -> usage. @@ -591,7 +597,7 @@ realtime_start([{remote, Remote}], []) -> not_changed -> error_msg("Realtime replication to cluster ~p is already started or not enabled!~n", [Remote]); {ok, _} -> - [text(io_lib:format("Realtime replication to cluster ~p started.~n", [Remote]))] + [text_msg("Realtime replication to cluster ~p started.~n", [Remote])] end; realtime_start([], [{all, _}]) -> ?LOG_USER_CMD("Start Realtime Replication to all connected clusters", []), @@ -609,7 +615,7 @@ realtime_stop([{remote, Remote}], []) -> not_changed -> error_msg("Realtime replication to cluster ~p is already stopped or not enabled!~n", [Remote]); {ok, _} -> - [text(io_lib:format("Realtime replication to cluster ~p stopped.~n", [Remote]))] + [text_msg("Realtime replication to cluster ~p stopped.~n", [Remote])] end; realtime_stop([], [{all, _}]) -> ?LOG_USER_CMD("Stop Realtime Replication to all connected clusters", []), @@ -625,7 +631,7 @@ realtime_cascades_enable([], []) -> ?LOG_USER_CMD("Enable Realtime Replication cascading", []), riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, always), - text(io_lib:format("Realtime cascades enabled.~n")); + text_msg("Realtime cascades enabled.~n"); realtime_cascades_enable(_,_) -> usage. @@ -637,7 +643,7 @@ realtime_cascades_disable([], []) -> ?LOG_USER_CMD("Disable Realtime Replication cascading", []), riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, never), - text(io_lib:format("Realtime cascades disabled.~n")); + text_msg("Realtime cascades disabled.~n"); realtime_cascades_disable(_,_) -> usage. @@ -648,9 +654,9 @@ realtime_cascades_disable(_,_) -> realtime_cascades_show([], []) -> case app_helper:get_env(riak_repl, realtime_cascades, always) of always -> - text(io_lib:format("Realtime cascades are enabled.~n")); + text_msg("Realtime cascades are enabled.~n"); never -> - text(io_lib:format("Realtime cascades are disabled.~n")) + text_msg("Realtime cascades are disabled.~n") end; realtime_cascades_show(_, _) -> usage. @@ -666,7 +672,7 @@ fullsync_enable([{remote, Remote}], []) -> riak_core_ring_manager:ring_trans(fun riak_repl_ring:fs_enable_trans/2, Remote), _ = riak_repl2_fscoordinator_sup:start_coord(Leader, Remote), - text(io_lib:format("Fullsync replication to cluster ~p enabled.", [Remote])); + text_msg("Fullsync replication to cluster ~p enabled.", [Remote]); fullsync_enable(_, _) -> usage. @@ -680,7 +686,7 @@ fullsync_disable([{remote, Remote}], []) -> riak_core_ring_manager:ring_trans(fun riak_repl_ring:fs_disable_trans/2, Remote), _ = riak_repl2_fscoordinator_sup:stop_coord(Leader, Remote), - text(io_lib:format("Fullsync replication to cluster ~p disabled.", [Remote])); + text_msg("Fullsync replication to cluster ~p disabled.", [Remote]); fullsync_disable(_, _) -> usage. @@ -702,7 +708,7 @@ fullsync_start([{remote, Remote}], []) -> "Use 'fullsync enable ~p' before start~n", [Remote, Remote]); Pid -> riak_repl2_fscoordinator:start_fullsync(Pid), - text(io_lib:format("Fullsync replication to cluster ~p started.", [Remote])) + text_msg("Fullsync replication to cluster ~p started.", [Remote]) end; fullsync_start([], [{all,_}]) -> Leader = riak_core_cluster_mgr:get_leader(), @@ -728,7 +734,7 @@ fullsync_stop([{remote, Remote}], []) -> error_msg("Fullsync is not enabled for cluster ~p.", [Remote]); Pid -> riak_repl2_fscoordinator:stop_fullsync(Pid), - text(io_lib:format("Fullsync stopped for cluster ~p.", [Remote])) + text_msg("Fullsync stopped for cluster ~p.", [Remote]) end; fullsync_stop([], [{all,_}]) -> Leader = riak_core_cluster_mgr:get_leader(), @@ -748,7 +754,7 @@ proxy_get_enable([{remote, Remote}], []) -> ?LOG_USER_CMD("Enable Riak CS Proxy GET block provider for ~p",[Remote]), riak_core_ring_manager:ring_trans(fun riak_repl_ring:pg_enable_trans/2, Remote), - text(io_lib:format("Proxy-get to cluster ~s has been enabled.", [Remote])); + text_msg("Proxy-get to cluster ~s has been enabled.", [Remote]); proxy_get_enable(_, _) -> usage. @@ -761,7 +767,7 @@ proxy_get_disable([{remote, Remote}], []) -> ?LOG_USER_CMD("Disable Riak CS Proxy GET block provider for ~p",[Remote]), riak_core_ring_manager:ring_trans(fun riak_repl_ring:pg_disable_trans/2, Remote), - text(io_lib:format("Proxy-get to cluster ~s has been disabled.", [Remote])); + text_msg("Proxy-get to cluster ~s has been disabled.", [Remote]); proxy_get_disable(_, _) -> usage. @@ -773,7 +779,7 @@ proxy_get_redirect_cluster_id([], []) -> {ok, Ring} = riak_core_ring_manager:get_my_ring(), ClusterId = lists:flatten( io_lib:format("~p", [riak_core_ring:cluster_name(Ring)])), - text(io:format("local cluster id: ~p~n", [ClusterId])); + text_msg("local cluster id: ~p~n", [ClusterId]); proxy_get_redirect_cluster_id(_, _) -> usage. @@ -784,9 +790,9 @@ proxy_get_redirect_cluster_id(_, _) -> proxy_get_redirect_show([{from, FromClusterId}], []) -> case riak_core_metadata:get({<<"replication">>, <<"cluster-mapping">>}, FromClusterId) of undefined -> - text(io_lib:format("No mapping for ~p~n", [FromClusterId])); + text_msg("No mapping for ~p~n", [FromClusterId]); ToClusterId -> - text(io_lib:format("Cluster id ~p redirecting to cluster id ~p~n", [FromClusterId, ToClusterId])) + text_msg("Cluster id ~p redirecting to cluster id ~p~n", [FromClusterId, ToClusterId]) end; proxy_get_redirect_show(_, _) -> usage. @@ -801,8 +807,8 @@ proxy_get_redirect_add([{from, FromClusterId}, {to, ToClusterId}], []) -> lager:info("Redirecting cluster id: ~p to ~p", [FromClusterId, ToClusterId]), riak_core_metadata:put({<<"replication">>, <<"cluster-mapping">>}, FromClusterId, ToClusterId), - text(io_lib:format("Redirected proxy-get from cluster ~s to cluster ~s~n", - [FromClusterId, ToClusterId])); + text_msg("Redirected proxy-get from cluster ~s to cluster ~s~n", + [FromClusterId, ToClusterId]); proxy_get_redirect_add(_, _) -> usage. @@ -814,6 +820,6 @@ proxy_get_redirect_add(_, _) -> proxy_get_redirect_delete([{from, FromClusterId}], []) -> lager:info("Deleting redirect to ~p", [FromClusterId]), riak_core_metadata:delete({<<"replication">>, <<"cluster-mapping">>}, FromClusterId), - text(io_lib:format("Deleted proxy-get redirect from cluster ~s~n", [FromClusterId])); + text_msg("Deleted proxy-get redirect from cluster ~s~n", [FromClusterId]); proxy_get_redirect_delete(_, _) -> usage. From 9e17edec23833ca72b6fd324365085ab193fcdfa Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Thu, 19 Feb 2015 11:06:02 -0600 Subject: [PATCH 22/37] Minor ws cleanup --- priv/riak_repl.schema | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/riak_repl.schema b/priv/riak_repl.schema index 441317cb..c285858c 100644 --- a/priv/riak_repl.schema +++ b/priv/riak_repl.schema @@ -104,7 +104,7 @@ " intervals or use 'never'"); Global == per_sink -> [{list_to_atom(SinkName), Value} || {["mdc", "fullsync", "interval", SinkName], Value} <- Sinks ]; - true -> + true -> cuttlefish:invalid("Cannot set both mdc.fullsync.interval and" " sink-specific intervals") end From 71fa2de3b33f8ec18fd18b45ed4469c066709281 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Thu, 19 Feb 2015 11:06:16 -0600 Subject: [PATCH 23/37] WIP ensure all calls to generate output using text_msg include deprecation/upgrade warnings --- src/riak_repl_console13.erl | 79 +++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index a2f60d94..5bfcd45e 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -364,14 +364,27 @@ output(CmdOut) -> CmdOut] end. + +%% error_out(Str) -> +%% error_out(Str, []). + +error_out(Fmt, Args) -> + output(error_msg(Fmt, Args)). + error_msg(Fmt, Args) -> [alert(text_msg(Fmt, Args))]. +text_out(Str) -> + text_out(Str, []). + +text_out(Str, Args) -> + output(text_msg(Str, Args)). + text_msg(Str) -> text_msg(Str, []). text_msg(Fmt, Args) -> - text(io_lib:format(Fmt, Args)). + [text(io_lib:format(Fmt, Args))]. %%----------------------- %% Command: clusterstats @@ -391,7 +404,7 @@ clusterstats(_, Flags) -> throw(badflags) end, %% TODO: make this output better - output(text_msg("~p~n", [CMStats ++ CConnStats])) + text_out("~p~n", [CMStats ++ CConnStats]) catch throw:badflags -> usage end. @@ -446,11 +459,11 @@ cluster_mgr_stats() -> %% Command: clustername %%----------------------- clustername([], []) -> - output(text_msg("Cluster name: ~s~n", [riak_core_connection:symbolic_clustername()])); + text_out("Cluster name: ~s~n", [riak_core_connection:symbolic_clustername()]); clustername([], [{name, ClusterName}]) -> riak_core_ring_manager:ring_trans(fun riak_core_connection:set_symbolic_clustername/2, ClusterName), - output(text_msg("Cluster name was set to: ~s~n", [ClusterName])). + text_out("Cluster name was set to: ~s~n", [ClusterName]). %%----------------------- %% Command: clusters @@ -534,11 +547,11 @@ connect([{address, {IP, Port}}], []) -> %% TODO: This should return an error, not a bare status, %% but we still want to be able to print to stderr. This %% will require a clique enhancement. - error_msg("Error: Unable to establish connections until local cluster is named.~n" + error_out("Error: Unable to establish connections until local cluster is named.~n" "First use ~s clustername --name NAME ~n", [script_name()]); _Name -> riak_core_cluster_mgr:add_remote_cluster({IP, Port}), - [text_msg("Connecting to remote cluster at ~p:~p.", [IP, Port])] + text_out("Connecting to remote cluster at ~p:~p.", [IP, Port]) end; connect(_, _) -> usage. @@ -550,11 +563,11 @@ connect(_, _) -> disconnect([{remote, {IP, Port}}], []) -> ?LOG_USER_CMD("Disconnect from cluster at ~p:~p", [IP, Port]), riak_core_cluster_mgr:remove_remote_cluster({IP, Port}), - [text_msg("Disconnecting from cluster at ~p:~p~n", [IP, Port])]; + text_out("Disconnecting from cluster at ~p:~p~n", [IP, Port]); disconnect([{remote, Name}], []) -> ?LOG_USER_CMD("Disconnect from cluster ~p", [Name]), riak_core_cluster_mgr:remove_remote_cluster(Name), - [text_msg("Disconnecting from cluster ~p~n", [Name])]; + text_out("Disconnecting from cluster ~p~n", [Name]); disconnect(_, _) -> usage. @@ -567,9 +580,9 @@ realtime_enable([{remote, Remote}], []) -> ?LOG_USER_CMD("Enable Realtime Replication to cluster ~p", [Remote]), case riak_repl2_rt:enable(Remote) of not_changed -> - [error_msg("Realtime replication to cluster ~p already enabled!~n", [Remote])]; + error_out("Realtime replication to cluster ~p already enabled!~n", [Remote]); {ok, _} -> - [text_msg("Realtime replication to cluster ~p enabled.~n", [Remote])] + text_out("Realtime replication to cluster ~p enabled.~n", [Remote]) end; realtime_enable(_, _) -> usage. @@ -581,9 +594,9 @@ realtime_disable([{remote, Remote}], []) -> ?LOG_USER_CMD("Disable Realtime Replication to cluster ~p", [Remote]), case riak_repl2_rt:disable(Remote) of not_changed -> - error_msg("Realtime replication to cluster ~p already disabled!~n", [Remote]); + error_out("Realtime replication to cluster ~p already disabled!~n", [Remote]); {ok, _} -> - [text_msg("Realtime replication to cluster ~p disabled.~n", [Remote])] + text_out("Realtime replication to cluster ~p disabled.~n", [Remote]) end; realtime_disable(_, _) -> usage. @@ -595,9 +608,9 @@ realtime_start([{remote, Remote}], []) -> ?LOG_USER_CMD("Start Realtime Replication to cluster ~p", [Remote]), case riak_repl2_rt:start(Remote) of not_changed -> - error_msg("Realtime replication to cluster ~p is already started or not enabled!~n", [Remote]); + error_out("Realtime replication to cluster ~p is already started or not enabled!~n", [Remote]); {ok, _} -> - [text_msg("Realtime replication to cluster ~p started.~n", [Remote])] + text_out("Realtime replication to cluster ~p started.~n", [Remote]) end; realtime_start([], [{all, _}]) -> ?LOG_USER_CMD("Start Realtime Replication to all connected clusters", []), @@ -613,9 +626,9 @@ realtime_stop([{remote, Remote}], []) -> ?LOG_USER_CMD("Stop Realtime Replication to cluster ~p", [Remote]), case riak_repl2_rt:stop(Remote) of not_changed -> - error_msg("Realtime replication to cluster ~p is already stopped or not enabled!~n", [Remote]); + error_out("Realtime replication to cluster ~p is already stopped or not enabled!~n", [Remote]); {ok, _} -> - [text_msg("Realtime replication to cluster ~p stopped.~n", [Remote])] + text_out("Realtime replication to cluster ~p stopped.~n", [Remote]) end; realtime_stop([], [{all, _}]) -> ?LOG_USER_CMD("Stop Realtime Replication to all connected clusters", []), @@ -631,7 +644,7 @@ realtime_cascades_enable([], []) -> ?LOG_USER_CMD("Enable Realtime Replication cascading", []), riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, always), - text_msg("Realtime cascades enabled.~n"); + text_out("Realtime cascades enabled.~n"); realtime_cascades_enable(_,_) -> usage. @@ -643,7 +656,7 @@ realtime_cascades_disable([], []) -> ?LOG_USER_CMD("Disable Realtime Replication cascading", []), riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, never), - text_msg("Realtime cascades disabled.~n"); + text_out("Realtime cascades disabled.~n"); realtime_cascades_disable(_,_) -> usage. @@ -654,9 +667,9 @@ realtime_cascades_disable(_,_) -> realtime_cascades_show([], []) -> case app_helper:get_env(riak_repl, realtime_cascades, always) of always -> - text_msg("Realtime cascades are enabled.~n"); + text_out("Realtime cascades are enabled.~n"); never -> - text_msg("Realtime cascades are disabled.~n") + text_out("Realtime cascades are disabled.~n") end; realtime_cascades_show(_, _) -> usage. @@ -672,7 +685,7 @@ fullsync_enable([{remote, Remote}], []) -> riak_core_ring_manager:ring_trans(fun riak_repl_ring:fs_enable_trans/2, Remote), _ = riak_repl2_fscoordinator_sup:start_coord(Leader, Remote), - text_msg("Fullsync replication to cluster ~p enabled.", [Remote]); + text_out("Fullsync replication to cluster ~p enabled.", [Remote]); fullsync_enable(_, _) -> usage. @@ -686,7 +699,7 @@ fullsync_disable([{remote, Remote}], []) -> riak_core_ring_manager:ring_trans(fun riak_repl_ring:fs_disable_trans/2, Remote), _ = riak_repl2_fscoordinator_sup:stop_coord(Leader, Remote), - text_msg("Fullsync replication to cluster ~p disabled.", [Remote]); + text_out("Fullsync replication to cluster ~p disabled.", [Remote]); fullsync_disable(_, _) -> usage. @@ -704,11 +717,11 @@ fullsync_start([{remote, Remote}], []) -> %% io:format("Fullsync not enabled for cluster ~p~n", [Remote]), %% io:format("Use 'fullsync enable ~p' before start~n", [Remote]), %% {error, not_enabled}; - error_msg("Fullsync not enabled for cluster ~p~n" + error_out("Fullsync not enabled for cluster ~p~n" "Use 'fullsync enable ~p' before start~n", [Remote, Remote]); Pid -> riak_repl2_fscoordinator:start_fullsync(Pid), - text_msg("Fullsync replication to cluster ~p started.", [Remote]) + text_out("Fullsync replication to cluster ~p started.", [Remote]) end; fullsync_start([], [{all,_}]) -> Leader = riak_core_cluster_mgr:get_leader(), @@ -731,10 +744,10 @@ fullsync_stop([{remote, Remote}], []) -> case proplists:get_value(Remote, Fullsyncs) of undefined -> %% Fullsync is not enabled, but carry on quietly. - error_msg("Fullsync is not enabled for cluster ~p.", [Remote]); + error_out("Fullsync is not enabled for cluster ~p.", [Remote]); Pid -> riak_repl2_fscoordinator:stop_fullsync(Pid), - text_msg("Fullsync stopped for cluster ~p.", [Remote]) + text_out("Fullsync stopped for cluster ~p.", [Remote]) end; fullsync_stop([], [{all,_}]) -> Leader = riak_core_cluster_mgr:get_leader(), @@ -754,7 +767,7 @@ proxy_get_enable([{remote, Remote}], []) -> ?LOG_USER_CMD("Enable Riak CS Proxy GET block provider for ~p",[Remote]), riak_core_ring_manager:ring_trans(fun riak_repl_ring:pg_enable_trans/2, Remote), - text_msg("Proxy-get to cluster ~s has been enabled.", [Remote]); + text_out("Proxy-get to cluster ~s has been enabled.", [Remote]); proxy_get_enable(_, _) -> usage. @@ -767,7 +780,7 @@ proxy_get_disable([{remote, Remote}], []) -> ?LOG_USER_CMD("Disable Riak CS Proxy GET block provider for ~p",[Remote]), riak_core_ring_manager:ring_trans(fun riak_repl_ring:pg_disable_trans/2, Remote), - text_msg("Proxy-get to cluster ~s has been disabled.", [Remote]); + text_out("Proxy-get to cluster ~s has been disabled.", [Remote]); proxy_get_disable(_, _) -> usage. @@ -779,7 +792,7 @@ proxy_get_redirect_cluster_id([], []) -> {ok, Ring} = riak_core_ring_manager:get_my_ring(), ClusterId = lists:flatten( io_lib:format("~p", [riak_core_ring:cluster_name(Ring)])), - text_msg("local cluster id: ~p~n", [ClusterId]); + text_out("local cluster id: ~p~n", [ClusterId]); proxy_get_redirect_cluster_id(_, _) -> usage. @@ -790,9 +803,9 @@ proxy_get_redirect_cluster_id(_, _) -> proxy_get_redirect_show([{from, FromClusterId}], []) -> case riak_core_metadata:get({<<"replication">>, <<"cluster-mapping">>}, FromClusterId) of undefined -> - text_msg("No mapping for ~p~n", [FromClusterId]); + text_out("No mapping for ~p~n", [FromClusterId]); ToClusterId -> - text_msg("Cluster id ~p redirecting to cluster id ~p~n", [FromClusterId, ToClusterId]) + text_out("Cluster id ~p redirecting to cluster id ~p~n", [FromClusterId, ToClusterId]) end; proxy_get_redirect_show(_, _) -> usage. @@ -807,7 +820,7 @@ proxy_get_redirect_add([{from, FromClusterId}, {to, ToClusterId}], []) -> lager:info("Redirecting cluster id: ~p to ~p", [FromClusterId, ToClusterId]), riak_core_metadata:put({<<"replication">>, <<"cluster-mapping">>}, FromClusterId, ToClusterId), - text_msg("Redirected proxy-get from cluster ~s to cluster ~s~n", + text_out("Redirected proxy-get from cluster ~s to cluster ~s~n", [FromClusterId, ToClusterId]); proxy_get_redirect_add(_, _) -> usage. @@ -820,6 +833,6 @@ proxy_get_redirect_add(_, _) -> proxy_get_redirect_delete([{from, FromClusterId}], []) -> lager:info("Deleting redirect to ~p", [FromClusterId]), riak_core_metadata:delete({<<"replication">>, <<"cluster-mapping">>}, FromClusterId), - text_msg("Deleted proxy-get redirect from cluster ~s~n", [FromClusterId]); + text_out("Deleted proxy-get redirect from cluster ~s~n", [FromClusterId]); proxy_get_redirect_delete(_, _) -> usage. From fa615ffe1c188901d63a8ac0284d6e208d7d99e5 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Fri, 20 Feb 2015 08:39:14 -0600 Subject: [PATCH 24/37] Minor formatting tweak --- src/riak_repl_console13.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index 5bfcd45e..8b9fdc2b 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -331,14 +331,14 @@ upgrade(["fullsync", Command]=Args) when Command == "start"; upgrade_warning(Args, "Use `fullsync ~s --all`", [Command]), ["fullsync", Command, "--all"]; upgrade(["fullsync", Key]=Args) when Key == "max_fssource_node"; - Key == "max_fssource_cluster"; - Key == "max_fssink_node" -> + Key == "max_fssource_cluster"; + Key == "max_fssink_node" -> TKey = config_key_translation(Key), upgrade_warning(Args, "Use `show ~s`", [TKey]), ["show", TKey]; upgrade(["fullsync", Key, Value]=Args) when Key == "max_fssource_node"; - Key == "max_fssource_cluster"; - Key == "max_fssink_node" -> + Key == "max_fssource_cluster"; + Key == "max_fssink_node" -> TKey = config_key_translation(Key), upgrade_warning(Args, "Use `show ~s`", [TKey]), ["set", TKey++"="++Value]; From e24b8e417015c228b1783213134d5b52e3a8042a Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Fri, 20 Feb 2015 08:39:57 -0600 Subject: [PATCH 25/37] Cleanup some unused functions --- src/riak_repl_console13.erl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index 8b9fdc2b..4452bf32 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -354,7 +354,6 @@ config_key_translation("max_fssource_node") -> "mdc.fullsync.source.max_worke config_key_translation("max_fssource_cluster") -> "mdc.fullsync.source.max_workers_per_cluster"; config_key_translation("max_fssink_node") -> "mdc.fullsync.sink.max_workers_per_node". - output(CmdOut) -> case get(upgrade_warning) of undefined -> CmdOut; @@ -364,10 +363,6 @@ output(CmdOut) -> CmdOut] end. - -%% error_out(Str) -> -%% error_out(Str, []). - error_out(Fmt, Args) -> output(error_msg(Fmt, Args)). @@ -380,9 +375,6 @@ text_out(Str) -> text_out(Str, Args) -> output(text_msg(Str, Args)). -text_msg(Str) -> - text_msg(Str, []). - text_msg(Fmt, Args) -> [text(io_lib:format(Fmt, Args))]. From a0d5dc61457fda9a6fbae2f573b3289fd218b56f Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Fri, 20 Feb 2015 09:08:28 -0600 Subject: [PATCH 26/37] Add max_fs* configs --- src/riak_repl_console13.erl | 53 +++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index 4452bf32..21d80ad3 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -18,8 +18,7 @@ register() -> ok = register_commands(), ok = register_usage(), - %% TODO: add max_fs* settings - %% ok = register_configs(), + ok = register_configs(), ok. register_commands() -> @@ -157,6 +156,18 @@ register_usage() -> ok = register_usage(["proxy-get", "redirect", "cluster-id"], proxy_get_redirect_usage()). +register_configs() -> + %% "mdc.fullsync.source.max_workers_per_node" + %% "mdc.fullsync.source.max_workers_per_cluster" + %% "mdc.fullsync.sink.max_workers_per_node" + Keys = ["mdc.fullsync.source.max_workers_per_node", + "mdc.fullsync.source.max_workers_per_cluster", + "mdc.fullsync.sink.max_workers_per_node"], + [ ok = clique:register_config(cuttlefish_variable:parse(Key), + fun set_fullsync_limit/3) || Key <- Keys ], + ok = clique:register_config_whitelist(Keys), + ok. + -spec commands_usage() -> string(). commands_usage() -> " Version 3 Commands:\n" @@ -828,3 +839,41 @@ proxy_get_redirect_delete([{from, FromClusterId}], []) -> text_out("Deleted proxy-get redirect from cluster ~s~n", [FromClusterId]); proxy_get_redirect_delete(_, _) -> usage. + +%%-------------------------- +%% Command: set FULLSYNC_CONFIG_KEY=VALUE +%%-------------------------- +set_fullsync_limit(["mdc", "fullsync", "source", "max_workers_per_node"], + Value, _Flags) -> + riak_core_util:rpc_every_member(lager, log, + [notice, [{pid, self()}], + "Locally set max number of Fullsync source workers to ~p", [Value]], + ?CONSOLE_RPC_TIMEOUT), + riak_core_util:rpc_every_member(application, set_env, + [riak_repl, max_fsssource_node, Value], + ?CONSOLE_RPC_TIMEOUT), + io:format("Set max number of fullsync workers per source node to ~p~n", [Value]); + +set_fullsync_limit(["mdc", "fullsync", "source", "max_workers_per_cluster"], + Value, _Flags) -> + riak_core_util:rpc_every_member(lager, log, + [notice, [{pid, self()}], + "Locally set max number of Fullsync workers " + "for Source cluster to ~p", [Value]], ?CONSOLE_RPC_TIMEOUT), + riak_core_util:rpc_every_member(application, set_env, + [riak_repl, max_fsssource_cluster, Value], + ?CONSOLE_RPC_TIMEOUT), + io:format("Set max number of fullsync workers for source" + " cluster to ~p~n", [Value]); + +set_fullsync_limit(["mdc", "fullsync", "sink", "max_workers_per_node"], + Value, _Flags) -> + riak_core_util:rpc_every_member(lager, log, + [notice, [{pid, self()}], + "Locally set max number of Fullsync sink workers to ~p", [Value]], + ?CONSOLE_RPC_TIMEOUT), + riak_core_util:rpc_every_member(application, set_env, + [riak_repl, max_fssink_node, Value], + ?CONSOLE_RPC_TIMEOUT), + io:format("Set max number of fullsync workers per sink node to ~p~n", [Value]). + From bf67b4d1d15755d4960d212f5df1fdf9ac65bdb0 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Fri, 20 Feb 2015 09:33:57 -0600 Subject: [PATCH 27/37] Refactor config max_fs* settings --- src/riak_repl_console13.erl | 41 ++++++++++++++----------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index 21d80ad3..d19f1e94 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -843,37 +843,26 @@ proxy_get_redirect_delete(_, _) -> %%-------------------------- %% Command: set FULLSYNC_CONFIG_KEY=VALUE %%-------------------------- -set_fullsync_limit(["mdc", "fullsync", "source", "max_workers_per_node"], - Value, _Flags) -> +set_fullsync_limit(["mdc", "fullsync"|Key], Value, _Flags) -> + %% NB: All config settings are done cluster-wide, there's not + %% flags for specific nodes like in handoff. + AppEnvKey = max_fs_config_key(Key), + Message = max_fs_message(AppEnvKey), riak_core_util:rpc_every_member(lager, log, [notice, [{pid, self()}], - "Locally set max number of Fullsync source workers to ~p", [Value]], + "[user] Locally set max number of fullsync workers ~s to ~p", + [Message, Value]], ?CONSOLE_RPC_TIMEOUT), riak_core_util:rpc_every_member(application, set_env, - [riak_repl, max_fsssource_node, Value], + [riak_repl, max_fssource_node, Value], ?CONSOLE_RPC_TIMEOUT), - io:format("Set max number of fullsync workers per source node to ~p~n", [Value]); + io:format("Set max number of fullsync workers ~s to ~p~n", [Message, Value]). -set_fullsync_limit(["mdc", "fullsync", "source", "max_workers_per_cluster"], - Value, _Flags) -> - riak_core_util:rpc_every_member(lager, log, - [notice, [{pid, self()}], - "Locally set max number of Fullsync workers " - "for Source cluster to ~p", [Value]], ?CONSOLE_RPC_TIMEOUT), - riak_core_util:rpc_every_member(application, set_env, - [riak_repl, max_fsssource_cluster, Value], - ?CONSOLE_RPC_TIMEOUT), - io:format("Set max number of fullsync workers for source" - " cluster to ~p~n", [Value]); +max_fs_message(max_fssource_node) -> "per source node"; +max_fs_message(max_fssource_cluster) -> "for source cluster"; +max_fs_message(max_fssink_node) -> "per sink node". -set_fullsync_limit(["mdc", "fullsync", "sink", "max_workers_per_node"], - Value, _Flags) -> - riak_core_util:rpc_every_member(lager, log, - [notice, [{pid, self()}], - "Locally set max number of Fullsync sink workers to ~p", [Value]], - ?CONSOLE_RPC_TIMEOUT), - riak_core_util:rpc_every_member(application, set_env, - [riak_repl, max_fssink_node, Value], - ?CONSOLE_RPC_TIMEOUT), - io:format("Set max number of fullsync workers per sink node to ~p~n", [Value]). +max_fs_config_key(["source", "max_workers_per_node"]) -> max_fssource_node; +max_fs_config_key(["source", "max_workers_per_cluster"]) -> max_fssource_cluster; +max_fs_config_key(["sink", "max_workers_per_node"]) -> max_fssink_node. From 979237f2b46d8a612528eb3685c0ce50c94de799 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Sat, 28 Feb 2015 09:39:51 -0700 Subject: [PATCH 28/37] Add nat-map commands --- src/riak_repl_console13.erl | 163 +++++++++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 3 deletions(-) diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index d19f1e94..d9977b02 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -104,6 +104,19 @@ register_commands() -> [{from, [{datatype, string}]}, {to, [{datatype, string}]}], [], fun proxy_get_redirect_delete/2), + ok = register_command(["nat-map", "show"], + [], [], + fun nat_map_show/2), + ok = register_command(["nat-map", "add"], + [{external, [{datatype, [ip, string]}]}, + {internal, [{datatype, string}]}], + [], + fun nat_map_add/2), + ok = register_command(["nat-map", "delete"], + [{external, [{datatype, [ip, string]}]}, + {internal, [{datatype, string}]}], + [], + fun nat_map_delete/2), ok. @@ -153,7 +166,10 @@ register_usage() -> ok = register_usage(["proxy-get", "redirect", "show"], fun proxy_get_redirect_show_usage/0), ok = register_usage(["proxy-get", "redirect", "add"], fun proxy_get_redirect_add_delete_usage/0), ok = register_usage(["proxy-get", "redirect", "delete"], fun proxy_get_redirect_add_delete_usage/0), - ok = register_usage(["proxy-get", "redirect", "cluster-id"], proxy_get_redirect_usage()). + ok = register_usage(["proxy-get", "redirect", "cluster-id"], proxy_get_redirect_usage()), + ok = register_usage(["nat-map"], nat_map_usage()), + ok = register_usage(["nat-map", "add"], nat_map_add_del_usage()), + ok = register_usage(["nat-map", "delete"], nat_map_add_del_usage()). register_configs() -> @@ -272,6 +288,20 @@ proxy_get_redirect_add_delete_usage() -> " DESTINATION must correspond to the result from the `" ++ script_name() ++ "\n" " proxy-get redirect cluster-id` command.". +nat_map_usage() -> + "nat-map SUBCOMMAND\n\n" + " Manipulate NAT mappings. NAT mappings allow replication connections\n" + " to traverse firewalls between private networks on previously\n" + " configured ports.\n\n" + " Sub-commands:\n" + " add Add a NAT mapping\n" + " delete Delete a NAT mapping\n" + " show Display the NAT mapping table". + +nat_map_add_del_usage() -> + "nat-map ( add | delete ) external=EXTERNAL_IF internal=INTERNAL_IP\n\n" + " Add or delete a NAT mapping from the given external IP to the given internal" + " IP. An optional external port can be supplied.". upgrade(["clustername", [$-|_]|_]=Args) -> %% Don't upgrade a call that includes a flag @@ -353,6 +383,29 @@ upgrade(["fullsync", Key, Value]=Args) when Key == "max_fssource_node"; TKey = config_key_translation(Key), upgrade_warning(Args, "Use `show ~s`", [TKey]), ["set", TKey++"="++Value]; +upgrade(["nat-map", Command, External0, Internal0]=Args0) when Command == "add"; + Command == "delete" -> + + {External, EChanged} = case string:words(External0, "=") of + 2 -> {"external="++External0, true}; + _ -> {External0, false} + end, + {Internal, IChanged} = case string:words(Internal0, "=") of + 2 -> {"internal="++Internal0, true}; + _ -> {Internal0, false} + end, + Args = [Command, External, Internal], + if EChanged orelse IChanged -> + upgrade_warning(Args0, "Use `nat-map ~s ~s ~s`", Args), + ok; + true -> + ok + end, + ["nat-map"|Args]; +upgrade(["nat-map", "del"|Rest]) -> + %% TODO: should we include this warning or just silently pass through? + %% upgrade_warning(Args, "Use `nat-map delete ~s`", [string:join(" ", Rest)]), + upgrade(["nat-map", "delete"|Rest]); upgrade(Args) -> Args. @@ -840,6 +893,109 @@ proxy_get_redirect_delete([{from, FromClusterId}], []) -> proxy_get_redirect_delete(_, _) -> usage. +%%-------------------------- +%% Command: nat-map show +%%-------------------------- +nat_map_show([], []) -> + Ring = riak_repl_console:get_ring(), + Headers = [{internal, "Internal"}, + {external, "External"}], + Rows = [ format_nat_map(Int, Ext) || + {Int, Ext} <- riak_repl_ring:get_nat_map(Ring)], + output([text("NAT mappings:\n"), table([Headers|Rows])]); +nat_map_show(_,_) -> + usage. + +format_nat_map(Int, Ext) -> + [{internal, io_lib:format("~s", [print_ip_and_maybe_port(Int)])}, + {external, io_lib:format("~s", [print_ip_and_maybe_port(Ext)])}]. + +print_ip_and_maybe_port({IP, Port}) when is_tuple(IP) -> + [inet_parse:ntoa(IP), $:, integer_to_list(Port)]; +print_ip_and_maybe_port({Host, Port}) when is_list(Host) -> + [Host, $:, integer_to_list(Port)]; +print_ip_and_maybe_port(IP) when is_tuple(IP) -> + inet_parse:ntoa(IP); +print_ip_and_maybe_port(Host) when is_list(Host) -> + Host. + +%%-------------------------- +%% Command: nat-map add external=IP:PORT internal=IP +%%-------------------------- +nat_map_add([{external, Ext0}, {internal, Int0}], []) -> + %% We rely on cuttlefish to parse the IP for the most + %% part. However, it leaves addreses as strings so we still need + %% to try parsing them. + Ext = parse_ip(Ext0, false), + Int = parse_ip(Int0, true), + case collect_bad_ips([Ext, Int]) of + [] -> + ?LOG_USER_CMD("Add a NAT map from External IP ~p to Internal IP ~p", [Ext, Int]), + riak_core_ring_manager:ring_trans(fun riak_repl_ring:add_nat_map/2, {Ext, Int}), + text_out("Added a NAT map from External IP ~p to Internal IP ~p~n", [Ext, Int]); + Errors -> + error_out("Invalid IPs given: ~s~n", [string:join(", ", Errors)]) + end; +nat_map_add([{internal,_}, {external,_}]=Args, []) -> + nat_map_add(lists:sort(Args), []); +nat_map_add(_,_) -> + usage. + +parse_ip({Addr, Port}, HostnameAllowed) when is_list(Addr), is_integer(Port) -> + case parse_ip(Addr, HostnameAllowed) of + {error,_}=E -> E; + IP -> + {IP, Port} + end; +parse_ip(Addr, true) when is_list(Addr) -> + case parse_ip(Addr, false) of + {error,_}=E -> + case inet_gethost_native:gethostbyname(Addr) of + {ok, HostAddr} -> HostAddr; + _ -> E + end; + IP -> IP + end; +parse_ip(Addr, false) when is_list(Addr) -> + case inet_parse:ipv4strict_address(Addr) of + {ok, IP} -> IP; + _ -> + {error, {bad_ip, Addr}} + end. + +collect_bad_ips(List) -> + [ case BadIP of + {IP, Port} -> string:join(":", [IP, Port]); + _ -> BadIP + end || {error, {bad_ip, BadIP}} <- List]. + +%%-------------------------- +%% Command: nat-map delete external=IP:PORT internal=IP +%%-------------------------- + +nat_map_delete([{external, Ext0}, {internal, Int0}], []) -> + %% We rely on cuttlefish to parse the IP for the most + %% part. However, it leaves addreses as strings so we still need + %% to try parsing them. + %% We rely on cuttlefish to parse the IP for the most + %% part. However, it leaves addreses as strings so we still need + %% to try parsing them. + Ext = parse_ip(Ext0, false), + Int = parse_ip(Int0, true), + case collect_bad_ips([Ext, Int]) of + [] -> + ?LOG_USER_CMD("Delete a NAT map from External IP ~p to Internal IP ~p", [Ext, Int]), + riak_core_ring_manager:ring_trans(fun riak_repl_ring:del_nat_map/2, {Ext, Int}), + text_out("Deleted a NAT map from External IP ~p to Internal IP ~p~n", [Ext, Int]); + Errors -> + error_out("Invalid IPs given: ~s~n", [string:join(", ", Errors)]) + end; +nat_map_delete([{internal,_}, {external,_}]=Args, []) -> + nat_map_delete(lists:sort(Args), []); +nat_map_delete(_,_) -> + usage. + + %%-------------------------- %% Command: set FULLSYNC_CONFIG_KEY=VALUE %%-------------------------- @@ -862,7 +1018,8 @@ max_fs_message(max_fssource_node) -> "per source node"; max_fs_message(max_fssource_cluster) -> "for source cluster"; max_fs_message(max_fssink_node) -> "per sink node". -max_fs_config_key(["source", "max_workers_per_node"]) -> max_fssource_node; + +max_fs_config_key(["source", "max_workers_per_node"]) -> max_fssource_node; max_fs_config_key(["source", "max_workers_per_cluster"]) -> max_fssource_cluster; -max_fs_config_key(["sink", "max_workers_per_node"]) -> max_fssink_node. +max_fs_config_key(["sink", "max_workers_per_node"]) -> max_fssink_node. From b67384cc54b2fe8d46f130014a7a21e5509bb553 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Wed, 4 Mar 2015 14:18:22 -0600 Subject: [PATCH 29/37] Cleanup riak_repl_console exports and expose registration callback --- src/riak_repl_console.erl | 715 +++--------------------------------- src/riak_repl_console13.erl | 14 +- 2 files changed, 58 insertions(+), 671 deletions(-) diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index bf9a305c..28846df4 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -2,12 +2,24 @@ %% Copyright 2007-2015 Basho Technologies, Inc. All Rights Reserved. -module(riak_repl_console). -include("riak_repl.hrl"). --export([client_stats_rpc/0, server_stats_rpc/0]). +-behaviour(clique_handler). + +%% Clique behavior +-export([register_cli/0]). + +%% Main entry-point for script +-export([command/1]). + +%% Utility functions for script integration +-export([script_name/0, + register_command/4, + register_usage/2]). + +%% Stats functions +-export([client_stats_rpc/0, + server_stats_rpc/0]). -export([extract_rt_fs_send_recv_kbps/1]). --export([ - fullsync/1, proxy_get/1 - ]). -export([rt_remotes_status/0, fs_remotes_status/0]). @@ -17,26 +29,17 @@ server_stats/0, coordinator_stats/0, coordinator_srv_stats/0]). --export([modes/1, set_modes/1, get_modes/0, - max_fssource_node/1, - max_fssource_cluster/1, - max_fssink_node/1, - %% realtime_cascades/1, - %% cascades/1, - show_nat_map/1, - add_nat_map/1, - del_nat_map/1 %, - %% add_block_provider_redirect/1, - %% show_block_provider_redirect/1, - %% show_local_cluster_id/1, - %% delete_block_provider_redirect/1 - ]). +%% Modes functions +-export([modes/1, set_modes/1, get_modes/0]). + +%% Ring utilities -export([get_ring/0, maybe_set_ring/2]). --export([command/1]). --export([script_name/0, register_command/4, register_usage/2]). +-spec register_cli() -> ok. +register_cli() -> + riak_repl_console13:register_cli(). -spec script_name() -> string(). script_name() -> @@ -79,257 +82,6 @@ command([Script|Args]) -> OkOrError end. - - -%% command([_Script, "status"]) -> -%% %% TODO: how does 'quiet' even work? -%% status([]); -%% command([_Script, "clusterstats"|Params]) when length(Params) =< 1 -> -%% clusterstats(Params); -%% command([_Script, "clustername"|Params]) when length(Params) =< 1 -> -%% clustername(Params); -%% command([_Script, "connections"]) -> -%% connections([]); -%% command([_Script, "clusters"]) -> -%% clusters([]); -%% command([_Script, "connect"|[_]=Params]) -> -%% connect(Params); -%% command([_Script, "disconnect"|[_]=Params]) -> -%% disconnect(Params); -%% command([_Script, "modes", "show"]) -> -%% modes([]); -%% command([_Script, "modes", "set"|Params]) when length(Params) >= 1 -> -%% modes(Params); -%% command([_Script, "realtime"|["enable",_]=Params]) -> -%% realtime(Params); -%% command([_Script, "realtime"|["disable",_]=Params]) -> -%% realtime(Params); -%% command([_Script, "realtime"|["start"|_]=Params]) when length(Params) =< 2 -> -%% realtime(Params); -%% command([_Script, "realtime"|["stop"|_]=Params]) when length(Params) =< 2 -> -%% realtime(Params); -%% command([_Script, "realtime", "cascades"|[_]=Params]) -> -%% realtime_cascades(Params); -%% command([_Script, "fullsync"|["enable",_]=Params]) -> -%% fullsync(Params); -%% command([_Script, "fullsync"|["disable",_]=Params]) -> -%% fullsync(Params); -%% command([_Script, "fullsync"|["start"|_]=Params]) when length(Params) =< 2 -> -%% fullsync(Params); -%% command([_Script, "fullsync"|["stop"|_]=Params]) when length(Params) =< 2 -> -%% fullsync(Params); -%% command([_Script, "fullsync", "source", "max_workers_per_cluster", "show"]) -> -%% max_fssource_cluster([]); -%% command([_Script, "fullsync", "source", "max_workers_per_cluster", "set"|[_]=Params]) -> -%% max_fssource_cluster(Params); -%% command([_Script, "fullsync", "source", "max_workers_per_node", "show"]) -> -%% max_fssource_node([]); -%% command([_Script, "fullsync", "source", "max_workers_per_node", "set"|[_]=Params]) -> -%% max_fssource_node(Params); -%% command([_Script, "fullsync", "sink", "max_workers_per_node", "show"]) -> -%% max_fssink_node([]); -%% command([_Script, "fullsync", "sink", "max_workers_per_node", "set"|[_]=Params]) -> -%% max_fssink_node(Params); -%% command([_Script, "proxy-get"|["enable",_]=Params]) -> -%% proxy_get(Params); -%% command([_Script, "proxy-get"|["disable",_]=Params]) -> -%% proxy_get(Params); -%% command([_Script, "proxy-get", "redirect", "show"|[_]=Params]) -> -%% show_block_provider_redirect(Params); -%% command([_Script, "proxy-get", "redirect", "add"|[_,_]=Params]) -> -%% add_block_provider_redirect(Params); -%% command([_Script, "proxy-get", "redirect", "delete"|[_]=Params]) -> -%% delete_block_provider_redirect(Params); -%% command([_Script, "proxy-get", "redirect", "cluster-id"]) -> -%% show_local_cluster_id([]); -%% command([_Script, "nat-map", "show"]) -> -%% show_nat_map([]); -%% command([_Script, "nat-map", "add"|[_,_]=Params]) -> -%% add_nat_map(Params); -%% command([_Script, "nat-map", "delete"|[_,_]=Params]) -> -%% del_nat_map(Params); -%% command([Script|Params]) -> -%% usage(Script, Params), -%% error. - --spec usage_out(string(), iodata()) -> ok. -usage_out(Script, Desc) -> - io:format("Usage: ~s ~s~n", [Script, Desc]). - --spec usage(string(), [string()]) -> ok. -usage(Script, ["clusterstats"|_]) -> - usage_out(Script, "clusterstats [ | : ]"); -usage(Script, ["clustername"|_]) -> - usage_out(Script, "clustername [ ]"); -usage(Script, ["connect"|_]) -> - usage_out(Script, "connect :"); -usage(Script, ["disconnect"|_]) -> - usage_out(Script, "disconnect ( : | )"); -usage(Script, ["modes"|_]) -> - usage_out(Script, - "modes [ ... ]\n\n" - " Sub-commands:\n" - " show Show the active replication modes\n" - " set Set the active replication modes"); -%% usage(Script, ["realtime"|Params]) -> -%% realtime_usage(Script, Params); -usage(Script, ["fullsync"|Params]) -> - fullsync_usage(Script, Params); -usage(Script, ["proxy-get"|Params]) -> - proxy_get_usage(Script, Params); -usage(Script, ["nat-map"|Params]) -> - nat_map_usage(Script, Params); -usage(Script, _) -> - EnabledModes = get_modes(), - ModeHelp = [{mode_repl13, riak_repl_console13}, - {mode_repl12, riak_repl_console12}], - ModesCommands = string:join([ Module:commands_usage() || - {Mode, Module} <- ModeHelp, - lists:member(Mode, EnabledModes) ], "\n\n"), - usage_out(Script, - [" [ ...]\n\n", - " Commands:\n" - " modes Show or set replication modes\n" - " status Display status and metrics\n\n", - ModesCommands]). - -%% realtime_usage(Script, ["cascades"|_]) -> -%% usage_out(Script, -%% ["realtime cascades \n\n" -%% " Manipulate cascading realtime replication. When this cluster is a\n" -%% " sink and is receiving realtime replication, it can propagate\n" -%% " incoming writes to any clusters for which it is a source and\n" -%% " realtime replication is enabled.\n\n" -%% " Sub-commands:\n" -%% " enable Enable cascading realtime replication\n" -%% " disable Disable cascading realtime replication\n" -%% " show Show the current cascading realtime replication setting"] -%% ); -%% realtime_usage(Script, _) -> -%% usage_out(Script, -%% ["realtime [ ...]\n\n" -%% " Manipulate realtime replication. Realtime replication streams\n" -%% " incoming writes on the source cluster to the sink cluster(s).\n\n" -%% " Sub-commands:\n" -%% " enable Enable realtime replication\n" -%% " disable Disable realtime replication\n" -%% " start Start realtime replication\n" -%% " stop Stop realtime replication\n" -%% " cascades Manipulate cascading realtime replication"] -%% ). - -fullsync_usage(Script, ["max_fssink_node"|_]) -> - fullsync_usage(Script, ["sink"]); -fullsync_usage(Script, ["max_fssource_node"|_]) -> - fullsync_usage(Script, ["source"]); -fullsync_usage(Script, ["max_fssource_cluster"|_]) -> - fullsync_usage(Script, ["source"]); -fullsync_usage(Script, ["source"|_]) -> - usage_out(Script, - ["fullsync source []\n\n" - " Set limits on the number of fullsync workers on a source cluster. If\n" - " is omitted, the current setting is displayed.\n\n" - " Available settings:\n" - " max_workers_per_node\n" - " max_workers_per_cluster" - ]); -fullsync_usage(Script, ["sink"|_]) -> - usage_out(Script, - ["fullsync sink max_workers_per_node []\n\n" - " Set limits on the number of fullsync workers on a sink cluster. If\n" - " is omitted, the current setting is displayed." - ]); -fullsync_usage(Script, _) -> - usage_out(Script, - ["fullsync [ ...]\n\n" - " Manipulate fullsync replication. Fullsync replication compares data\n" - " on the source and the sink and then sends detected differences to\n" - " the sink cluster.\n\n" - " Sub-commands:\n" - " enable Enable fullsync replication\n" - " disable Disable fullsync replication\n" - " start Start fullsync replication\n" - " stop Stop fullsync replication\n" - " source Manipulate source cluster limits\n" - " sink Manipulate sink cluster limits" - ]). - -proxy_get_usage(Script, ["enable"|_]) -> - usage_out(Script, - ["proxy-get enable \n\n" - " Enables proxy-get requests from to this source\n" - " cluster."]); -proxy_get_usage(Script, ["disable"|_]) -> - usage_out(Script, - ["proxy-get disable \n\n" - " Disables proxy-get requests from to this source\n" - " cluster."]); -proxy_get_usage(Script, ["redirect", "add"|_]) -> - usage_out(Script, - ["proxy-get redirect add \n\n" - " Add a proxy-get redirection to a new cluster. Arguments\n" - " and must correspond to the result\n" - " from the `", Script, " proxy-get redirect cluster-id` command.\n" - ]); -proxy_get_usage(Script, ["redirect", "del"|_]) -> - usage_out(Script, - ["proxy-get redirect delete \n\n" - " Delete a proxy-get redirection. Arguments and\n" - " must correspond to the result from the `", Script, "\n", - " proxy-get redirect cluster-id` command." - ]); -proxy_get_usage(Script, ["redirect", "show"|_]) -> - usage_out(Script, - ["proxy-get redirect show \n\n" - " Show an existing proxy-get redirection. Argument \n" - " must correspond to the result from the `", Script, "proxy-get redirect\n" - " cluster-id` command." - ]); -proxy_get_usage(Script, ["redirect"|_]) -> - usage_out(Script, - ["proxy-get redirect [ ... ]\n\n" - " Manipulate proxy-get redirection functionality. Redirection allows\n" - " existing proxy-get connections to be redirected to new source\n" - " clusters so that the original source cluster can be decommissioned.\n\n" - " Sub-commands:\n" - " add Add a proxy-get redirection\n" - " delete Delete an existing proxy-get redirection\n" - " show Show a proxy-get redirection\n" - " cluster-id Display the local cluster's identifier"]); -proxy_get_usage(Script, _) -> - usage_out(Script, - ["proxy-get [ ... ]\n\n" - " Manipulate proxy-get functionality. Proxy-get allows sink clusters\n" - " to actively fetch remote objects over a realtime replication\n" - " connection. Currently, this is only used by Riak CS.\n" - " Sub-commands:\n" - " enable Enable proxy-get on the source\n" - " disable Disable proxy-get on the source\n" - " redirect Manipulation proxy-get redirection"]). - -nat_map_usage(Script, ["add"|_]) -> - usage_out(Script, - ["nat-map add [:] \n\n" - " Add a NAT mapping from the given external IP to the given internal" - " IP. An optional external port can be supplied."]); -nat_map_usage(Script, ["delete"|_]) -> - usage_out(Script, - ["nat-map delete [:] \n\n" - " Delete a NAT mapping between the given external IP and the given" - " internal IP."]); -nat_map_usage(Script, _) -> - usage_out(Script, - ["nat-map [ ...]\n\n" - " Manipulate NAT mappings. NAT mappings allow replication connections\n" - " to traverse firewalls between private networks on previously\n" - " configured ports.\n\n" - " Sub-commands:\n" - " add Add a NAT mapping\n" - " delete Delete a NAT mapping\n" - " show Display the NAT mapping table" - ]). - - set_modes(Modes) -> Ring = get_ring(), NewRing = riak_repl_ring:set_modes(Ring, Modes), @@ -395,211 +147,6 @@ cluster_fs_running(Sink) -> ClusterCoord = riak_repl2_fscoordinator_sup:coord_for_cluster(Sink), riak_repl2_fscoordinator:is_running(ClusterCoord). - -%% -%% Repl2 commands -%% -%% Show cluster stats for this node -%% clusterstats([]) -> -%% %% connection manager stats -%% CMStats = cluster_mgr_stats(), -%% CConnStats = riak_core_connection_mgr_stats:get_consolidated_stats(), -%% Stats = CMStats ++ CConnStats, -%% io:format("~p~n", [Stats]); -%% %% slice cluster stats by remote "IP:Port" or "protocol-id". -%% %% Example protocol-id is rt_repl -%% clusterstats([Arg]) -> -%% NWords = string:words(Arg, $:), -%% case NWords of -%% 1 -> -%% %% assume protocol-id -%% ProtocolId = list_to_atom(Arg), -%% CConnStats = riak_core_connection_mgr_stats:get_stats_by_protocol(ProtocolId), -%% CMStats = cluster_mgr_stats(), -%% Stats = CMStats ++ CConnStats, -%% io:format("~p~n", [Stats]); -%% 2 -> -%% Address = Arg, -%% IP = string:sub_word(Address, 1, $:), -%% PortStr = string:sub_word(Address, 2, $:), -%% {Port,_Rest} = string:to_integer(PortStr), -%% CConnStats = riak_core_connection_mgr_stats:get_stats_by_ip({IP,Port}), -%% CMStats = cluster_mgr_stats(), -%% Stats = CMStats ++ CConnStats, -%% io:format("~p~n", [Stats]); -%% _ -> -%% {error, {badarg, Arg}} -%% end. - -%% TODO: cluster naming belongs in riak_core_ring, not in riak_core_connection, but -%% not until we move all of the connection stuff to core. -%% clustername([]) -> -%% MyName = riak_core_connection:symbolic_clustername(), -%% io:format("~s~n", [MyName]), -%% ok; -%% clustername([ClusterName]) -> -%% ?LOG_USER_CMD("Set clustername to ~p", [ClusterName]), -%% riak_core_ring_manager:ring_trans(fun riak_core_connection:set_symbolic_clustername/2, -%% ClusterName), -%% ok. - -%% connect([Address]) -> -%% ?LOG_USER_CMD("Connect to cluster at ~p", [Address]), -%% NWords = string:words(Address, $:), -%% case NWords of -%% 2 -> -%% IP = string:sub_word(Address, 1, $:), -%% PortStr = string:sub_word(Address, 2, $:), -%% connect([IP, PortStr]); -%% _ -> -%% io:format("Error: remote connection is missing port. Expected 'connect '~n"), -%% {error, {badarg, Address}} -%% end; -%% connect([IP, PortStr]) -> -%% ?LOG_USER_CMD("Connect to cluster at ~p:~p", [IP, PortStr]), -%% {Port,_Rest} = string:to_integer(PortStr), -%% case riak_core_connection:symbolic_clustername() of -%% "undefined" -> -%% io:format("Error: Unable to establish connections until local cluster is named.~n"), -%% io:format("First use 'riak-repl clustername '~n"), -%% {error, undefined_cluster_name}; -%% _Name -> -%% riak_core_cluster_mgr:add_remote_cluster({IP, Port}), -%% ok -%% end. - -%% remove a remote connection by clustername or by IP/Port address: -%% clustername -%% | ip:port -%% | ip port -%% disconnect([Address]) -> -%% ?LOG_USER_CMD("Disconnect from cluster at ~p", [Address]), -%% NWords = string:words(Address, $:), -%% case NWords of -%% 1 -> -%% Remote = Address, -%% %% TODO: need to wrap a single ring transition around all of these. -%% %% fullsync(["stop", Remote]), -%% %% fullsync(["disable", Remote]), -%% %% realtime(["stop", Remote]), -%% %% realtime(["disable", Remote]), -%% %% tear down cluster manager connection -%% riak_core_cluster_mgr:remove_remote_cluster(Remote), -%% ok; -%% 2 -> -%% IP = string:sub_word(Address, 1, $:), -%% PortStr = string:sub_word(Address, 2, $:), -%% _ = disconnect([IP, PortStr]), -%% ok; -%% _ -> -%% {error, {badarg, Address}} -%% end; -%% disconnect([IP, PortStr]) -> -%% ?LOG_USER_CMD("Disconnect from cluster at ~p:~p", [IP, PortStr]), -%% {Port,_Rest} = string:to_integer(PortStr), -%% riak_core_cluster_mgr:remove_remote_cluster({IP, Port}), -%% ok. - -%% realtime([Cmd, Remote]) -> -%% case Cmd of -%% "enable" -> -%% ?LOG_USER_CMD("Enable Realtime Replication to cluster ~p", [Remote]), -%% riak_repl2_rt:enable(Remote); -%% "disable" -> -%% ?LOG_USER_CMD("Disable Realtime Replication to cluster ~p", [Remote]), -%% riak_repl2_rt:disable(Remote); -%% "start" -> -%% ?LOG_USER_CMD("Start Realtime Replication to cluster ~p", [Remote]), -%% riak_repl2_rt:start(Remote); -%% "stop" -> -%% ?LOG_USER_CMD("Stop Realtime Replication to cluster ~p", [Remote]), -%% riak_repl2_rt:stop(Remote) -%% end, -%% ok; -%% realtime([Cmd]) -> -%% Remotes = riak_repl2_rt:enabled(), -%% _ = case Cmd of -%% "start" -> -%% ?LOG_USER_CMD("Start Realtime Replication to all connected clusters", -%% []), -%% _ = [riak_repl2_rt:start(Remote) || Remote <- Remotes]; -%% "stop" -> -%% ?LOG_USER_CMD("Stop Realtime Replication to all connected clusters", -%% []), -%% _ = [riak_repl2_rt:stop(Remote) || Remote <- Remotes] -%% end, -%% ok. - -fullsync([Cmd, Remote]) -> - Leader = riak_core_cluster_mgr:get_leader(), - case Cmd of - "enable" -> - ?LOG_USER_CMD("Enable Fullsync Replication to cluster ~p", [Remote]), - riak_core_ring_manager:ring_trans(fun - riak_repl_ring:fs_enable_trans/2, Remote), - _ = riak_repl2_fscoordinator_sup:start_coord(Leader, Remote), - ok; - "disable" -> - ?LOG_USER_CMD("Disable Fullsync Replication to cluster ~p", [Remote]), - riak_core_ring_manager:ring_trans(fun - riak_repl_ring:fs_disable_trans/2, Remote), - _ = riak_repl2_fscoordinator_sup:stop_coord(Leader, Remote), - ok; - "start" -> - ?LOG_USER_CMD("Start Fullsync Replication to cluster ~p", [Remote]), - Fullsyncs = riak_repl2_fscoordinator_sup:started(Leader), - case proplists:get_value(Remote, Fullsyncs) of - undefined -> - io:format("Fullsync not enabled for cluster ~p~n", [Remote]), - io:format("Use 'fullsync enable ~p' before start~n", [Remote]), - {error, not_enabled}; - Pid -> - riak_repl2_fscoordinator:start_fullsync(Pid), - ok - end; - "stop" -> - ?LOG_USER_CMD("Stop Fullsync Replication to cluster ~p", [Remote]), - Fullsyncs = riak_repl2_fscoordinator_sup:started(Leader), - case proplists:get_value(Remote, Fullsyncs) of - undefined -> - %% Fullsync is not enabled, but carry on quietly. - ok; - Pid -> - riak_repl2_fscoordinator:stop_fullsync(Pid), - ok - end - end; -fullsync([Cmd]) -> - Leader = riak_core_cluster_mgr:get_leader(), - Fullsyncs = riak_repl2_fscoordinator_sup:started(Leader), - case Cmd of - "start" -> - ?LOG_USER_CMD("Start Fullsync Replication to all connected clusters",[]), - _ = [riak_repl2_fscoordinator:start_fullsync(Pid) || {_, Pid} <- - Fullsyncs], - ok; - "stop" -> - ?LOG_USER_CMD("Stop Fullsync Replication to all connected clusters",[]), - _ = [riak_repl2_fscoordinator:stop_fullsync(Pid) || {_, Pid} <- - Fullsyncs], - ok - end, - ok. - -proxy_get([Cmd, Remote]) -> - case Cmd of - "enable" -> - ?LOG_USER_CMD("Enable Riak CS Proxy GET block provider for ~p",[Remote]), - riak_core_ring_manager:ring_trans(fun - riak_repl_ring:pg_enable_trans/2, Remote), - ok; - "disable" -> - ?LOG_USER_CMD("Disable Riak CS Proxy GET block provider for ~p",[Remote]), - riak_core_ring_manager:ring_trans(fun - riak_repl_ring:pg_disable_trans/2, Remote), - ok - end. - modes([]) -> CurrentModes = get_modes(), io:format("Current replication modes: ~p~n",[CurrentModes]), @@ -610,190 +157,6 @@ modes(NewModes) -> set_modes(Modes), modes([]). -%% realtime_cascades(["enable"]) -> -%% ?LOG_USER_CMD("Enable Realtime Replication cascading", []), -%% riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, -%% always), -%% io:format("Realtime cascades enabled.~n"); -%% realtime_cascades(["disable"]) -> -%% ?LOG_USER_CMD("Disable Realtime Replication cascading", []), -%% riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, -%% never), -%% io:format("Realtime cascades disabled.~n"); -%% realtime_cascades(["show"]) -> -%% case app_helper:get_env(riak_repl, realtime_cascades, always) of -%% always -> -%% io:format("Realtime cascades are enabled.~n"); -%% never -> -%% io:format("Realtime cascades are disabled.~n") -%% end. - -%% cascades(Val) -> -%% realtime_cascades(Val). - -%% For each of these "max" parameter changes, we need to make an rpc multi-call to every node -%% so that all nodes have the new value in their application environment. That way, whoever -%% becomes the fullsync coordinator will have the correct values. TODO: what happens when a -%% machine bounces and becomes leader? It won't know the new value. Seems like we need a central -%% place to hold these configuration values. -max_fssource_node([]) -> - %% show the default so as not to confuse the user - io:format("max_fssource_node value = ~p~n", - [app_helper:get_env(riak_repl, max_fssource_node, - ?DEFAULT_SOURCE_PER_NODE)]); -max_fssource_node([FSSourceNode]) -> - NewVal = erlang:list_to_integer(FSSourceNode), - riak_core_util:rpc_every_member(?MODULE, max_fssource_node, [NewVal], ?CONSOLE_RPC_TIMEOUT), - ?LOG_USER_CMD("Set max number of Fullsync workers per Source node to ~p",[NewVal]), - max_fssource_node([]), - ok; -max_fssource_node(NewVal) -> - ?LOG_USER_CMD("Locally set max number of Fullsync workers to ~p",[NewVal]), - application:set_env(riak_repl, max_fssource_node, NewVal). - -max_fssource_cluster([]) -> - %% show the default so as not to confuse the user - io:format("max_fssource_cluster value = ~p~n", - [app_helper:get_env(riak_repl, max_fssource_cluster, - ?DEFAULT_SOURCE_PER_CLUSTER)]); -max_fssource_cluster([FSSourceCluster]) -> - NewVal = erlang:list_to_integer(FSSourceCluster), - riak_core_util:rpc_every_member(?MODULE, max_fssource_cluster, [NewVal], ?CONSOLE_RPC_TIMEOUT), - ?LOG_USER_CMD("Set max number of Fullsync workers for Source cluster to ~p",[NewVal]), - max_fssource_cluster([]), - ok; -max_fssource_cluster(NewVal) -> - ?LOG_USER_CMD("Locally set max number of Fullsync workersfor Source cluster to ~p",[NewVal]), - application:set_env(riak_repl, max_fssource_cluster, NewVal). - -max_fssink_node([]) -> - io:format("max_fssink_node value = ~p~n", - [app_helper:get_env(riak_repl, max_fssink_node, ?DEFAULT_MAX_SINKS_NODE)]); -max_fssink_node([FSSinkNode]) -> - NewVal = erlang:list_to_integer(FSSinkNode), - riak_core_util:rpc_every_member(?MODULE, max_fssink_node, [NewVal], ?CONSOLE_RPC_TIMEOUT), - ?LOG_USER_CMD("Set max number of Fullsync works per Sink node to ~p",[NewVal]), - max_fssink_node([]), - ok; -max_fssink_node(NewVal) -> - ?LOG_USER_CMD("Locally set max number of Fullsync workers per Sink node to ~p",[NewVal]), - application:set_env(riak_repl, max_fssink_node, NewVal). - -show_nat_map([]) -> - Ring = get_ring(), - io:format("Nat map: ~n"), - [io:format(" ~-21.. s -> ~s~n", - [print_ip_and_maybe_port(Int), print_ip_and_maybe_port(Ext)]) - || {Int, Ext} <- riak_repl_ring:get_nat_map(Ring)]. - -add_nat_map([External, Internal]) -> - case {parse_ip_and_maybe_port(External, false), - parse_ip_and_maybe_port(Internal, true)} of - {{error, Reason}, _} -> - io:format("Bad external IP ~p", [Reason]), - error; - {_, {error, Reason}} -> - io:format("Bad internal IP ~p", [Reason]), - error; - {ExternalIP, InternalIP} -> - ?LOG_USER_CMD("Add a NAT map from External IP ~p to Internal IP ~p", [ExternalIP, InternalIP]), - riak_core_ring_manager:ring_trans( - fun riak_repl_ring:add_nat_map/2, - {ExternalIP, InternalIP}), - ok - end. - -del_nat_map([External, Internal]) -> - case {parse_ip_and_maybe_port(External, false), - parse_ip_and_maybe_port(Internal, true)} of - {{error, Reason}, _} -> - io:format("Bad external IP ~p", [Reason]), - error; - {_, {error, Reason}} -> - io:format("Bad internal IP ~p", [Reason]), - error; - {ExternalIP, InternalIP} -> - ?LOG_USER_CMD("Delete a NAT map from External IP ~p to Internal IP ~p", [ExternalIP, InternalIP]), - riak_core_ring_manager:ring_trans( - fun riak_repl_ring:del_nat_map/2, - {ExternalIP, InternalIP}), - ok - end. - -%% NB: the following commands are around the "Dead Cluster" redirect feature, -%% 306. They all operate using cluster_id (tuple), not clustername, for now, as -%% of this writing we had no reliable way to map a clustername to an id -%% over disterlang. When this API becomes available, this feature may use -%% it. -%% add_block_provider_redirect([FromClusterId, ToClusterId]) -> -%% lager:info("Redirecting cluster id: ~p to ~p", [FromClusterId, ToClusterId]), -%% riak_core_metadata:put({<<"replication">>, <<"cluster-mapping">>}, -%% FromClusterId, ToClusterId). - -%% show_block_provider_redirect([FromClusterId]) -> -%% case riak_core_metadata:get({<<"replication">>, <<"cluster-mapping">>}, FromClusterId) of -%% undefined -> -%% io:format("No mapping for ~p~n", [FromClusterId]); -%% ToClusterId -> -%% io:format("Cluster id ~p redirecting to cluster id ~p~n", [FromClusterId, ToClusterId]) -%% end. - -%% delete_block_provider_redirect([FromClusterId]) -> -%% lager:info("Deleting redirect to ~p", [FromClusterId]), -%% riak_core_metadata:delete({<<"replication">>, <<"cluster-mapping">>}, FromClusterId). - -%% show_local_cluster_id([]) -> -%% {ok, Ring} = riak_core_ring_manager:get_my_ring(), -%% ClusterId = lists:flatten( -%% io_lib:format("~p", [riak_core_ring:cluster_name(Ring)])), -%% io:format("local cluster id: ~p~n", [ClusterId]). - -%% helper functions - -parse_ip_and_maybe_port(String, Hostname) -> - case string:tokens(String, ":") of - [IPStr, PortStr] -> - case inet_parse:ipv4strict_address(IPStr) of - {ok, IP} -> - try list_to_integer(PortStr) of - Port -> - {IP, Port} - catch - _:_ -> - {error, {bad_port, PortStr}} - end; - _ when Hostname -> - case inet_gethost_native:gethostbyname(IPStr) of - {ok, _} -> - try list_to_integer(PortStr) of - Port -> - {IPStr, Port} - catch - _:_ -> - {error, {bad_port, PortStr}} - end; - _ -> - {error, {bad_ip, IPStr}} - end; - _ -> - {error, {bad_ip, IPStr}} - end; - [IPStr] -> - case inet_parse:ipv4strict_address(IPStr) of - {ok, IP} -> - IP; - _ when Hostname -> - case inet_gethost_native:gethostbyname(IPStr) of - {ok, _} -> - IPStr; - _ -> - {error, {bad_ip, IPStr}} - end; - _ -> - {error, {bad_ip, IPStr}} - end - end. - %% helper functions extract_rt_fs_send_recv_kbps(Most) -> @@ -804,14 +167,6 @@ extract_rt_fs_send_recv_kbps(Most) -> [{realtime_send_kbps, RTSendKbps}, {realtime_recv_kbps, RTRecvKbps}, {fullsync_send_kbps, FSSendKbps}, {fullsync_recv_kbps, FSRecvKbps}]. -print_ip_and_maybe_port({IP, Port}) when is_tuple(IP) -> - [inet_parse:ntoa(IP), $:, integer_to_list(Port)]; -print_ip_and_maybe_port({Host, Port}) when is_list(Host) -> - [Host, $:, integer_to_list(Port)]; -print_ip_and_maybe_port(IP) when is_tuple(IP) -> - inet_parse:ntoa(IP); -print_ip_and_maybe_port(Host) when is_list(Host) -> - Host. format_counter_stats([]) -> ok; format_counter_stats([{K,V}|T]) when is_list(K) -> @@ -959,6 +314,30 @@ server_stats() -> _ -> [] end. +rtq_stats() -> + case erlang:whereis(riak_repl2_rtq) of + Pid when is_pid(Pid) -> + [{realtime_queue_stats, riak_repl2_rtq:status()}]; + _ -> [] + end. + +cluster_mgr_stats() -> + case erlang:whereis(riak_repl_leader_gs) of + Pid when is_pid(Pid) -> + ConnectedClusters = case riak_core_cluster_mgr:get_known_clusters() of + {ok, Clusters} -> + [erlang:list_to_binary(Cluster) || Cluster <- + Clusters]; + Error -> Error + end, + [{cluster_name, + erlang:list_to_binary(riak_core_connection:symbolic_clustername())}, + {cluster_leader, riak_core_cluster_mgr:get_leader()}, + {connected_clusters, ConnectedClusters}]; + _ -> [] + end. + + server_stats_rpc() -> [server_stats(P) || P <- riak_repl_listener_sup:server_pids()]. diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index d9977b02..0c7d2723 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -2,7 +2,7 @@ %% 'mode_repl13'. -module(riak_repl_console13). -include("riak_repl.hrl"). --export([register/0, commands_usage/0, upgrade/1]). +-export([register_cli/0, commands_usage/0, upgrade/1]). -import(riak_repl_console, [register_command/4, script_name/0]). @@ -14,8 +14,8 @@ %% Interface %%----------------------- --spec register() -> ok. -register() -> +-spec register_cli() -> ok. +register_cli() -> ok = register_commands(), ok = register_usage(), ok = register_configs(), @@ -999,6 +999,14 @@ nat_map_delete(_,_) -> %%-------------------------- %% Command: set FULLSYNC_CONFIG_KEY=VALUE %%-------------------------- + +%% For each of these "max" parameter changes, we need to make an rpc +%% multi-call to every node so that all nodes have the new value in +%% their application environment. That way, whoever becomes the +%% fullsync coordinator will have the correct values. TODO: what +%% happens when a machine bounces and becomes leader? It won't know +%% the new value. Seems like we need a central place to hold these +%% configuration values. set_fullsync_limit(["mdc", "fullsync"|Key], Value, _Flags) -> %% NB: All config settings are done cluster-wide, there's not %% flags for specific nodes like in handoff. From f8f0093bfc5b4422adc8e6b1c17c620a3626d226 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Wed, 4 Mar 2015 14:55:23 -0600 Subject: [PATCH 30/37] Make sure to remove the script name from the pdict when done --- src/riak_repl_console.erl | 4 +++- src/riak_repl_console12.erl | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index 28846df4..2d687750 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -58,7 +58,9 @@ register_usage(Cmd, Usage) -> UsageStr = if is_function(Usage) -> Usage(); true -> Usage end, - [script_name(), " ", UsageStr] + ScriptName = script_name(), + erase(script_name), + [ScriptName, " ", UsageStr] end, clique:register_usage(["riak-repl"|Cmd], UsageFun). diff --git a/src/riak_repl_console12.erl b/src/riak_repl_console12.erl index ab0a63aa..47ef976e 100644 --- a/src/riak_repl_console12.erl +++ b/src/riak_repl_console12.erl @@ -13,7 +13,7 @@ pause_fullsync/1, resume_fullsync/1]). -import(riak_repl_console, - [script_name/0, register_usage/2, get_ring/0, + [register_usage/2, get_ring/0, maybe_set_ring/2]). -define(USAGE, From 5735d1cf03917f9dec87bb2a870c37a6b650d9fc Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Mon, 9 Mar 2015 16:49:14 -0500 Subject: [PATCH 31/37] Refactor status and modes commands --- src/riak_repl_cinfo.erl | 2 +- src/riak_repl_console.erl | 223 ++++++++++++++++++++++++-------------- 2 files changed, 142 insertions(+), 83 deletions(-) diff --git a/src/riak_repl_cinfo.erl b/src/riak_repl_cinfo.erl index a5ca9a1b..af419f47 100644 --- a/src/riak_repl_cinfo.erl +++ b/src/riak_repl_cinfo.erl @@ -24,4 +24,4 @@ cluster_info_generator_funs() -> ]. status(CPid) -> % CPid is the data collector's pid. - cluster_info:format(CPid, "~p\n", [riak_repl_console:status(quiet)]). + cluster_info:format(CPid, "~p\n", [riak_repl_console:status()]). diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index 2d687750..5464ad8d 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -16,8 +16,11 @@ register_usage/2]). %% Stats functions +-export([status/0]). % NB: Used by riak_repl_cinfo + -export([client_stats_rpc/0, server_stats_rpc/0]). + -export([extract_rt_fs_send_recv_kbps/1]). -export([rt_remotes_status/0, @@ -31,7 +34,7 @@ coordinator_srv_stats/0]). %% Modes functions --export([modes/1, set_modes/1, get_modes/0]). +-export([set_modes/1, get_modes/0]). %% Ring utilities -export([get_ring/0, maybe_set_ring/2]). @@ -39,7 +42,45 @@ -spec register_cli() -> ok. register_cli() -> - riak_repl_console13:register_cli(). + ok = riak_repl_console13:register_cli(), + ok = register_commands(), + ok = register_usage(). + +register_commands() -> + ok = register_command(["status"], [], [], fun status/2), + ok = register_command(["modes", "show"], [], [], fun modes_show/2), + ok = register_command(["modes", "set"], + [{mode_repl12, [{longname, "v2"}, + {datatype, flag}]}, + {mode_repl13, [{longname, "v3"}, + {datatype, flag}]}], + [], + fun modes_set/2). + +register_usage() -> + ok = register_usage([], fun repl_usage/0), + ok = register_usage(["modes"], modes_usage()), + ok. + +repl_usage() -> + EnabledModes = get_modes(), + ModeHelp = [{mode_repl13, riak_repl_console13:commands_usage()}, + {mode_repl12, riak_repl_console12:commands_usage()}], + ModesCommands = [ Commands || {Mode, Commands} <- ModeHelp, + lists:member(Mode, EnabledModes) ], + ["COMMAND [...]\n\n", + " Commands:\n", + " modes Show or set replication modes\n", + " status Display status and metrics\n\n", + ModesCommands]. + +modes_usage() -> + "modes ( show | set [ v2=(on|off) ] [ v3=(on|off) ] )\n\n" + " Manipulate active replication modes.\n\n" + " Subcommands:\n" + " show Shows the active replication modes.\n" + " set Toggles active replication modes.\n\n" + " When setting modes, omitting the mode name is the same as `off`. New clusters should use `mode_repl13` (Version 3) exclusively. Version 2 replication will be removed in a future release.". -spec script_name() -> string(). script_name() -> @@ -84,27 +125,26 @@ command([Script|Args]) -> OkOrError end. -set_modes(Modes) -> - Ring = get_ring(), - NewRing = riak_repl_ring:set_modes(Ring, Modes), - ok = maybe_set_ring(Ring, NewRing). - -get_modes() -> - Ring = get_ring(), - riak_repl_ring:get_modes(Ring). +%%----------------------- +%% Command: status +%%----------------------- +status([], []) -> + All = status(), + clique_status:list(lists:filtermap(fun format_counter_stat/1, All)); +status(_,_) -> + usage. -status([]) -> - status2(true); -status(quiet) -> - status2(false). - -status2(Verbose) -> +status() -> + %% NB: We export this for compatibility with previous components, + %% but all formatting for output is now done when preparing return + %% values for clique. Config = get_config(), + Ring = get_ring(), Stats1 = riak_repl_stats:get_stats(), - RTRemotesStatus = rt_remotes_status(), - FSRemotesStatus = fs_remotes_status(), - PGRemotesStatus = pg_remotes_status(), + RTRemotesStatus = rt_remotes_status(Ring), + FSRemotesStatus = fs_remotes_status(Ring), + PGRemotesStatus = pg_remotes_status(Ring), LeaderStats = leader_stats(), ClientStats = client_stats(), ServerStats = server_stats(), @@ -119,27 +159,26 @@ status2(Verbose) -> Stats1++LeaderStats++ClientStats++ServerStats++ CoordStats++CoordSrvStats++CMgrStats++RTQStats++PGStats, SendRecvKbps = extract_rt_fs_send_recv_kbps(Most), - All = Most ++ SendRecvKbps, - if Verbose -> - format_counter_stats(All); - true -> - All - end. + Most ++ SendRecvKbps. -pg_remotes_status() -> - Ring = get_ring(), + +pg_remotes_status(Ring) -> Enabled = string:join(riak_repl_ring:pg_enabled(Ring),", "), [{proxy_get_enabled, Enabled}]. rt_remotes_status() -> - Ring = get_ring(), + rt_remotes_status(get_ring()). + +rt_remotes_status(Ring) -> Enabled = string:join(riak_repl_ring:rt_enabled(Ring),", "), Started = string:join(riak_repl_ring:rt_started(Ring),", "), [{realtime_enabled, Enabled}, {realtime_started, Started}]. fs_remotes_status() -> - Ring = get_ring(), + fs_remotes_status(get_ring()). + +fs_remotes_status(Ring) -> Sinks = riak_repl_ring:fs_enabled(Ring), RunningSinks = [Sink || Sink <- Sinks, cluster_fs_running(Sink)], [{fullsync_enabled, string:join(Sinks, ", ")}, @@ -149,18 +188,6 @@ cluster_fs_running(Sink) -> ClusterCoord = riak_repl2_fscoordinator_sup:coord_for_cluster(Sink), riak_repl2_fscoordinator:is_running(ClusterCoord). -modes([]) -> - CurrentModes = get_modes(), - io:format("Current replication modes: ~p~n",[CurrentModes]), - ok; -modes(NewModes) -> - ?LOG_USER_CMD("Set replication mode(s) to ~p",[NewModes]), - Modes = [ list_to_atom(Mode) || Mode <- NewModes], - set_modes(Modes), - modes([]). - -%% helper functions - extract_rt_fs_send_recv_kbps(Most) -> RTSendKbps = sum_rt_send_kbps(Most), RTRecvKbps = sum_rt_recv_kbps(Most), @@ -169,32 +196,66 @@ extract_rt_fs_send_recv_kbps(Most) -> [{realtime_send_kbps, RTSendKbps}, {realtime_recv_kbps, RTRecvKbps}, {fullsync_send_kbps, FSSendKbps}, {fullsync_recv_kbps, FSRecvKbps}]. - -format_counter_stats([]) -> ok; -format_counter_stats([{K,V}|T]) when is_list(K) -> - io:format("~s: ~p~n", [K,V]), - format_counter_stats(T); -%%format_counter_stats([{K,V}|T]) when K == fullsync_coordinator -> -%% io:format("V = ~p",[V]), -%% case V of -%% [] -> io:format("~s: {}~n",[K]); -%% Val -> io:format("~s: ~s",[K,Val]) -%% end, -%% format_counter_stats(T); -format_counter_stats([{K,V}|T]) when K == client_rx_kbps; +%% Filters and formats stats for output. +format_counter_stat({K,V}) when K == client_rx_kbps; K == client_tx_kbps; K == server_rx_kbps; K == server_tx_kbps -> - io:format("~s: ~w~n", [K,V]), - format_counter_stats(T); -format_counter_stats([{K,V}|T]) -> - io:format("~p: ~p~n", [K,V]), - format_counter_stats(T); -format_counter_stats([{_K,_IPAddr,_V}|T]) -> - %% Don't include per-IP stats in this output - %% io:format("~p(~p): ~p~n", [K,IPAddr,V]), - format_counter_stats(T). + {true, io_lib:format("~s: ~w~n", [K,V])}; +format_counter_stat({K,V}) -> + {true, io_lib:format("~p: ~p~n", [K,V])}; +format_counter_stat(_) -> + %% NB: this covers the {_K,_IPAddr,_V} clause in the previous + %% version. + false. + +%%----------------------- +%% Command: modes show +%%----------------------- + +modes_show([], []) -> + CurrentModes = get_modes(), + [clique_status:text(io_lib:format("Currently enabled replication modes: ~s~n", + [modes_to_string(CurrentModes)]))]; +modes_show(_, _) -> + usage. + +%%----------------------- +%% Command: modes set [v2=on|off] [v3=on|off] +%%----------------------- + +modes_set([_|_]=InModes, []) -> + InvalidModes = [ I || {I,_} <- InModes, + not lists:keymember(I, 1, ?REPL_MODES) ], + if InvalidModes == [] -> + NewModes = [ M || {M, true} <- InModes ], + ?LOG_USER_CMD("Set replication mode(s) to ~p",[NewModes]), + set_modes(NewModes), + [clique_status:text(io_lib:format("Set enabled replication modes to: ~s", + [modes_to_string(NewModes)]))]; + true -> + [clique_status:alert(clique_status:text(io_lib:format("Invalid modes requested: ~p~n", [InvalidModes]))), + usage] + end; +modes_set(_,_) -> + usage. + +modes_to_string(Modes) -> + string:join([ mode_to_string(Mode) || Mode <- Modes ], ", "). + +mode_to_string(mode_repl12) -> "v2"; +mode_to_string(mode_repl13) -> "v3". + +set_modes(Modes) -> + Ring = get_ring(), + NewRing = riak_repl_ring:set_modes(Ring, Modes), + ok = maybe_set_ring(Ring, NewRing). + +get_modes() -> + Ring = get_ring(), + riak_repl_ring:get_modes(Ring). +%% helper functions maybe_set_ring(_R, _R) -> ok; maybe_set_ring(_R1, R2) -> RC = riak_repl_ring:get_repl_config(R2), @@ -215,24 +276,22 @@ get_config() -> undefined -> []; Repl -> - case dict:find(sites, Repl) of - error -> - []; - {ok, Sites} -> - lists:flatten([format_site(S) || S <- Sites]) - end ++ - case dict:find(listeners, Repl) of - error -> - []; - {ok, Listeners} -> - lists:flatten([format_listener(L) || L <- Listeners]) - end ++ - case dict:find(natlisteners, Repl) of - error -> - []; - {ok, NatListeners} -> - lists:flatten([format_nat_listener(L) || L <- NatListeners]) - end + lists:foldr( + fun({Key, Formatter}, Acc) -> + format_config(Repl, Key, Formatter) ++ Acc + end, + [], + [{sites, fun format_site/1}, + {listeners, fun format_listener/1}, + {natlisteners, fun format_nat_listener/1}]) + end. + +format_config(Repl, Key, Formatter) -> + case dict:find(Key, Repl) of + error -> + []; + {ok, List} -> + lists:flatmap(Formatter, List) end. format_site(S) -> @@ -289,7 +348,7 @@ client_stats() -> case erlang:whereis(riak_repl_leader_gs) of Pid when is_pid(Pid) -> %% NOTE: rpc:multicall to all clients removed - riak_repl_console:client_stats_rpc(); + ?MODULE:client_stats_rpc(); _ -> [] end. From a7797a765b71df520651b17571513c3cec18fe57 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Wed, 11 Mar 2015 09:48:44 -0500 Subject: [PATCH 32/37] Fix dialyzer and xref warnings in console modules Also update tools.mk --- src/riak_repl_console.erl | 37 ++--- src/riak_repl_console12.erl | 5 +- src/riak_repl_console13.erl | 271 ++++++++++++++++++------------------ tools.mk | 97 ++++++++++++- 4 files changed, 250 insertions(+), 160 deletions(-) diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index 5464ad8d..6f3dec72 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -31,7 +31,8 @@ client_stats/0, server_stats/0, coordinator_stats/0, - coordinator_srv_stats/0]). + coordinator_srv_stats/0, + cluster_mgr_stats/0]). %% Modes functions -export([set_modes/1, get_modes/0]). @@ -47,19 +48,20 @@ register_cli() -> ok = register_usage(). register_commands() -> - ok = register_command(["status"], [], [], fun status/2), - ok = register_command(["modes", "show"], [], [], fun modes_show/2), - ok = register_command(["modes", "set"], - [{mode_repl12, [{longname, "v2"}, - {datatype, flag}]}, - {mode_repl13, [{longname, "v3"}, - {datatype, flag}]}], - [], - fun modes_set/2). + true = register_command(["status"], [], [], fun status/2), + true = register_command(["modes", "show"], [], [], fun modes_show/2), + true = register_command(["modes", "set"], + [{mode_repl12, [{longname, "v2"}, + {datatype, flag}]}, + {mode_repl13, [{longname, "v3"}, + {datatype, flag}]}], + [], + fun modes_set/2), + ok. register_usage() -> - ok = register_usage([], fun repl_usage/0), - ok = register_usage(["modes"], modes_usage()), + true = register_usage([], fun repl_usage/0), + true = register_usage(["modes"], modes_usage()), ok. repl_usage() -> @@ -72,7 +74,7 @@ repl_usage() -> " Commands:\n", " modes Show or set replication modes\n", " status Display status and metrics\n\n", - ModesCommands]. + string:join(ModesCommands,"\n\n")]. modes_usage() -> "modes ( show | set [ v2=(on|off) ] [ v3=(on|off) ] )\n\n" @@ -89,11 +91,11 @@ script_name() -> Script -> Script end. --spec register_command([string()], list(), list(), fun()) -> ok. +-spec register_command([string()], [{atom(),[{_,_}]}], [{atom(),[{_,_}]}], fun()) -> true. register_command(Cmd, Keys, Flags, Fun) -> clique:register_command(["riak-repl"|Cmd], Keys, Flags, Fun). --spec register_usage([string()], iolist() | fun(() -> iolist())) -> ok. +-spec register_usage([string()], iolist() | fun(() -> iolist())) -> true. register_usage(Cmd, Usage) -> UsageFun = fun() -> UsageStr = if is_function(Usage) -> Usage(); @@ -103,7 +105,8 @@ register_usage(Cmd, Usage) -> erase(script_name), [ScriptName, " ", UsageStr] end, - clique:register_usage(["riak-repl"|Cmd], UsageFun). + %% TODO: specs are wrong on clique:register_usage/2 + clique_usage:register(["riak-repl"|Cmd], UsageFun). %% @doc Entry-point for all riak-repl commands. -spec command([string()]) -> ok | error. @@ -234,7 +237,7 @@ modes_set([_|_]=InModes, []) -> [clique_status:text(io_lib:format("Set enabled replication modes to: ~s", [modes_to_string(NewModes)]))]; true -> - [clique_status:alert(clique_status:text(io_lib:format("Invalid modes requested: ~p~n", [InvalidModes]))), + [clique_status:alert([clique_status:text(io_lib:format("Invalid modes requested: ~p~n", [InvalidModes]))]), usage] end; modes_set(_,_) -> diff --git a/src/riak_repl_console12.erl b/src/riak_repl_console12.erl index 47ef976e..21061995 100644 --- a/src/riak_repl_console12.erl +++ b/src/riak_repl_console12.erl @@ -77,9 +77,8 @@ commands_usage() -> %% @doc Registers usage output with Clique. register_usage() -> - _ = [ begin - register_usage([Cmd], [UsageStr, "\n\n", ?V2REPLDEP, "\n\n"]) - end || {Cmd, UsageStr} <- ?USAGE ], + _ = [ true = register_usage([Cmd], [UsageStr, "\n\n", ?V2REPLDEP, "\n\n"]) || + {Cmd, UsageStr} <- ?USAGE ], ok. add_listener(Params) -> diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index 0c7d2723..afb5290f 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -22,101 +22,101 @@ register_cli() -> ok. register_commands() -> - ok = register_command(["clusterstats"], [], - [{host, [{longname, "host"}, - {datatype, ip}]}, - {protocol, [{longname, "protocol"}, - {datatype, atom}]}], - fun clusterstats/2), - ok = register_command(["clustername"], [], - [{name, [{shortname, "n"}, - {longname, "name"}, - {datatype, string}]}], - fun clustername/2), - ok = register_command(["clusters"], [], [], fun clusters/2), - ok = register_command(["connections"], [], [], fun connections/2), - ok = register_command(["connect"], [{address, [{datatype, ip}]}], [], - fun connect/2), - ok = register_command(["disconnect"], [{remote, [{datatype, [ip, string]}]}], [], - fun disconnect/2), - ok = register_command(["realtime", "enable"], - [{remote, [{datatype, string}]}], - [], - fun realtime_enable/2), - ok = register_command(["realtime", "disable"], - [{remote, [{datatype, string}]}], - [], - fun realtime_disable/2), - ok = register_command(["realtime", "start"], - [{remote, [{datatype, string}]}], - [{all, [{longname, "all"}, - {shortname, "a"}]}], - fun realtime_start/2), - ok = register_command(["realtime", "stop"], - [{remote, [{datatype, string}]}], - [{all, [{longname, "all"}, - {shortname, "a"}]}], - fun realtime_stop/2), - ok = register_command(["realtime", "cascades", "enable"], - [],[], - fun realtime_cascades_enable/2), - ok = register_command(["realtime", "cascades", "disable"], - [],[], - fun realtime_cascades_disable/2), - ok = register_command(["realtime", "cascades", "show"], - [],[], - fun realtime_cascades_show/2), - ok = register_command(["fullsync", "enable"], - [{remote, [{datatype, string}]}], - [], - fun fullsync_enable/2), - ok = register_command(["fullsync", "disable"], - [{remote, [{datatype, string}]}], - [], - fun fullsync_disable/2), - ok = register_command(["fullsync", "start"], - [{remote, [{datatype, string}]}], - [{all, [{longname, "all"}, - {shortname, "a"}]}], - fun fullsync_start/2), - ok = register_command(["fullsync", "stop"], - [{remote, [{datatype, string}]}], - [{all, [{longname, "all"}, - {shortname, "a"}]}], - fun fullsync_stop/2), - ok = register_command(["proxy-get", "enable"], - [{remote, [{datatype, string}]}], - [], fun proxy_get_enable/2), - ok = register_command(["proxy-get", "disable"], - [{remote, [{datatype, string}]}], - [], fun proxy_get_disable/2), - ok = register_command(["proxy-get", "redirect", "cluster-id"], - [], - [], fun proxy_get_redirect_cluster_id/2), - ok = register_command(["proxy-get", "redirect", "show"], - [{from, [{datatype, string}]}], - [], fun proxy_get_redirect_show/2), - ok = register_command(["proxy-get", "redirect", "add"], - [{from, [{datatype, string}]}, - {to, [{datatype, string}]}], - [], fun proxy_get_redirect_add/2), - ok = register_command(["proxy-get", "redirect", "delete"], - [{from, [{datatype, string}]}, - {to, [{datatype, string}]}], - [], fun proxy_get_redirect_delete/2), - ok = register_command(["nat-map", "show"], - [], [], - fun nat_map_show/2), - ok = register_command(["nat-map", "add"], - [{external, [{datatype, [ip, string]}]}, - {internal, [{datatype, string}]}], - [], - fun nat_map_add/2), - ok = register_command(["nat-map", "delete"], - [{external, [{datatype, [ip, string]}]}, - {internal, [{datatype, string}]}], - [], - fun nat_map_delete/2), + true = register_command(["clusterstats"], [], + [{host, [{longname, "host"}, + {datatype, ip}]}, + {protocol, [{longname, "protocol"}, + {datatype, atom}]}], + fun clusterstats/2), + true = register_command(["clustername"], [], + [{name, [{shortname, "n"}, + {longname, "name"}, + {datatype, string}]}], + fun clustername/2), + true = register_command(["clusters"], [], [], fun clusters/2), + true = register_command(["connections"], [], [], fun connections/2), + true = register_command(["connect"], [{address, [{datatype, ip}]}], [], + fun connect/2), + true = register_command(["disconnect"], [{remote, [{datatype, [ip, string]}]}], [], + fun disconnect/2), + true = register_command(["realtime", "enable"], + [{remote, [{datatype, string}]}], + [], + fun realtime_enable/2), + true = register_command(["realtime", "disable"], + [{remote, [{datatype, string}]}], + [], + fun realtime_disable/2), + true = register_command(["realtime", "start"], + [{remote, [{datatype, string}]}], + [{all, [{longname, "all"}, + {shortname, "a"}]}], + fun realtime_start/2), + true = register_command(["realtime", "stop"], + [{remote, [{datatype, string}]}], + [{all, [{longname, "all"}, + {shortname, "a"}]}], + fun realtime_stop/2), + true = register_command(["realtime", "cascades", "enable"], + [],[], + fun realtime_cascades_enable/2), + true = register_command(["realtime", "cascades", "disable"], + [],[], + fun realtime_cascades_disable/2), + true = register_command(["realtime", "cascades", "show"], + [],[], + fun realtime_cascades_show/2), + true = register_command(["fullsync", "enable"], + [{remote, [{datatype, string}]}], + [], + fun fullsync_enable/2), + true = register_command(["fullsync", "disable"], + [{remote, [{datatype, string}]}], + [], + fun fullsync_disable/2), + true = register_command(["fullsync", "start"], + [{remote, [{datatype, string}]}], + [{all, [{longname, "all"}, + {shortname, "a"}]}], + fun fullsync_start/2), + true = register_command(["fullsync", "stop"], + [{remote, [{datatype, string}]}], + [{all, [{longname, "all"}, + {shortname, "a"}]}], + fun fullsync_stop/2), + true = register_command(["proxy-get", "enable"], + [{remote, [{datatype, string}]}], + [], fun proxy_get_enable/2), + true = register_command(["proxy-get", "disable"], + [{remote, [{datatype, string}]}], + [], fun proxy_get_disable/2), + true = register_command(["proxy-get", "redirect", "cluster-id"], + [], + [], fun proxy_get_redirect_cluster_id/2), + true = register_command(["proxy-get", "redirect", "show"], + [{from, [{datatype, string}]}], + [], fun proxy_get_redirect_show/2), + true = register_command(["proxy-get", "redirect", "add"], + [{from, [{datatype, string}]}, + {to, [{datatype, string}]}], + [], fun proxy_get_redirect_add/2), + true = register_command(["proxy-get", "redirect", "delete"], + [{from, [{datatype, string}]}, + {to, [{datatype, string}]}], + [], fun proxy_get_redirect_delete/2), + true = register_command(["nat-map", "show"], + [], [], + fun nat_map_show/2), + true = register_command(["nat-map", "add"], + [{external, [{datatype, [ip, string]}]}, + {internal, [{datatype, string}]}], + [], + fun nat_map_add/2), + true = register_command(["nat-map", "delete"], + [{external, [{datatype, [ip, string]}]}, + {internal, [{datatype, string}]}], + [], + fun nat_map_delete/2), ok. @@ -124,52 +124,53 @@ register_usage(Cmd, Usage) -> riak_repl_console:register_usage(Cmd, Usage). register_usage() -> - ok = register_usage(["clusterstats"], + true = register_usage(["clusterstats"], "clusterstats [ --protocol=PROTO | --host=IP:PORT ]\n\n" " Displays cluster statistics, optionally filtered by a protocol or host connection.\n\n" " Options:\n" " --protocol=PROTO Filters to a protocol where PROTO is one of:\n" " rt_repl, proxy_get, identity\n" " --host=IP:PORT Filters to a specific host, identified by IP and PORT"), - ok = register_usage(["clustername"], + true = register_usage(["clustername"], "clustername [ (-n | --name) NAME ]\n\n" " Shows or sets the symbolic clustername. Supplying the `-n` option sets the name.\n\n" " Options:\n" " -n NAME, --name NAME Sets the symbolic name to NAME"), - ok = register_usage(["clusters"], + true = register_usage(["clusters"], "clusters\n\n" " Displays information about known clusters."), - ok = register_usage(["connections"], + true = register_usage(["connections"], "connections\n\n" " Displays a list of current replication connections."), - ok = register_usage(["connect"], + true = register_usage(["connect"], "connect address=IP:PORT\n\n" " Connects to a remote cluster."), - ok = register_usage(["disconnect"], + true = register_usage(["disconnect"], "disconnect remote=(IP:PORT | NAME)\n\n" " Disconnects from a connected remote cluster."), - ok = register_usage(["realtime"], realtime_usage()), - ok = register_usage(["realtime", "enable"], realtime_enable_disable_usage()), - ok = register_usage(["realtime", "disable"], realtime_enable_disable_usage()), - ok = register_usage(["realtime", "start"], realtime_start_stop_usage()), - ok = register_usage(["realtime", "stop"], realtime_start_stop_usage()), - ok = register_usage(["realtime", "cascades"], realtime_cascades_usage()), - ok = register_usage(["fullsync"], fullsync_usage()), - ok = register_usage(["fullsync", "enable"], fullsync_enable_disable_usage()), - ok = register_usage(["fullsync", "disable"], fullsync_enable_disable_usage()), - ok = register_usage(["fullsync", "start"], fullsync_start_stop_usage()), - ok = register_usage(["fullsync", "stop"], fullsync_start_stop_usage()), - ok = register_usage(["proxy-get"], proxy_get_usage()), - ok = register_usage(["proxy-get", "enable"], proxy_get_enable_disable_usage()), - ok = register_usage(["proxy-get", "disable"], proxy_get_usage()), - ok = register_usage(["proxy-get", "redirect"], proxy_get_usage()), - ok = register_usage(["proxy-get", "redirect", "show"], fun proxy_get_redirect_show_usage/0), - ok = register_usage(["proxy-get", "redirect", "add"], fun proxy_get_redirect_add_delete_usage/0), - ok = register_usage(["proxy-get", "redirect", "delete"], fun proxy_get_redirect_add_delete_usage/0), - ok = register_usage(["proxy-get", "redirect", "cluster-id"], proxy_get_redirect_usage()), - ok = register_usage(["nat-map"], nat_map_usage()), - ok = register_usage(["nat-map", "add"], nat_map_add_del_usage()), - ok = register_usage(["nat-map", "delete"], nat_map_add_del_usage()). + true = register_usage(["realtime"], realtime_usage()), + true = register_usage(["realtime", "enable"], realtime_enable_disable_usage()), + true = register_usage(["realtime", "disable"], realtime_enable_disable_usage()), + true = register_usage(["realtime", "start"], realtime_start_stop_usage()), + true = register_usage(["realtime", "stop"], realtime_start_stop_usage()), + true = register_usage(["realtime", "cascades"], realtime_cascades_usage()), + true = register_usage(["fullsync"], fullsync_usage()), + true = register_usage(["fullsync", "enable"], fullsync_enable_disable_usage()), + true = register_usage(["fullsync", "disable"], fullsync_enable_disable_usage()), + true = register_usage(["fullsync", "start"], fullsync_start_stop_usage()), + true = register_usage(["fullsync", "stop"], fullsync_start_stop_usage()), + true = register_usage(["proxy-get"], proxy_get_usage()), + true = register_usage(["proxy-get", "enable"], proxy_get_enable_disable_usage()), + true = register_usage(["proxy-get", "disable"], proxy_get_usage()), + true = register_usage(["proxy-get", "redirect"], proxy_get_usage()), + true = register_usage(["proxy-get", "redirect", "show"], fun proxy_get_redirect_show_usage/0), + true = register_usage(["proxy-get", "redirect", "add"], fun proxy_get_redirect_add_delete_usage/0), + true = register_usage(["proxy-get", "redirect", "delete"], fun proxy_get_redirect_add_delete_usage/0), + true = register_usage(["proxy-get", "redirect", "cluster-id"], proxy_get_redirect_usage()), + true = register_usage(["nat-map"], nat_map_usage()), + true = register_usage(["nat-map", "add"], nat_map_add_del_usage()), + true = register_usage(["nat-map", "delete"], nat_map_add_del_usage()), + ok. register_configs() -> @@ -179,8 +180,8 @@ register_configs() -> Keys = ["mdc.fullsync.source.max_workers_per_node", "mdc.fullsync.source.max_workers_per_cluster", "mdc.fullsync.sink.max_workers_per_node"], - [ ok = clique:register_config(cuttlefish_variable:parse(Key), - fun set_fullsync_limit/3) || Key <- Keys ], + [true, true, true] = [ clique:register_config(cuttlefish_variable:tokenize(Key), + fun set_fullsync_limit/3) || Key <- Keys ], ok = clique:register_config_whitelist(Keys), ok. @@ -313,7 +314,7 @@ upgrade(["clusterstats", [$-|_]|_]=Args) -> %% Don't upgrade a call that includes a flag Args; upgrade(["clusterstats", Arg]=Args) -> - case string:words(Arg, ":") of + case string:words(Arg, $:) of 1 -> upgrade_warning(Args, "Use `clusterstats --protocol ~s`", [Arg]), ["clusterstats", "--protocol", Arg]; @@ -323,7 +324,7 @@ upgrade(["clusterstats", Arg]=Args) -> _ -> Args end; upgrade(["connect", Arg|Rest]=Args) -> - case string:words(Arg, "=") of + case string:words(Arg, $=) of 2 -> Args; 1 -> upgrade_warning(Args, "Use `connect address=~s`", [Arg]), @@ -331,7 +332,7 @@ upgrade(["connect", Arg|Rest]=Args) -> _ -> Args end; upgrade(["disconnect", Arg|Rest]=Args) -> - case string:words(Arg, "=") of + case string:words(Arg, $=) of 2 -> Args; 1 -> upgrade_warning(Args, "Use `disconnect remote=~s`", [Arg]), @@ -341,7 +342,7 @@ upgrade(["realtime", Command, Arg|Rest]=Args) when Command == "enable"; Command == "disable"; Command == "start"; Command == "stop" -> - case string:words(Arg, "=") of + case string:words(Arg, $=) of 2 -> Args; 1 -> upgrade_warning(Args, "Use `realtime ~s remote=~s`", [Command, Arg]), @@ -361,7 +362,7 @@ upgrade(["fullsync", Command, Arg|Rest]=Args) when Command == "enable"; Command == "disable"; Command == "start"; Command == "stop" -> - case string:words(Arg, "=") of + case string:words(Arg, $=) of 2 -> Args; 1 -> upgrade_warning(Args, "Use `fullsync ~s remote=~s`", [Command, Arg]), @@ -386,11 +387,11 @@ upgrade(["fullsync", Key, Value]=Args) when Key == "max_fssource_node"; upgrade(["nat-map", Command, External0, Internal0]=Args0) when Command == "add"; Command == "delete" -> - {External, EChanged} = case string:words(External0, "=") of + {External, EChanged} = case string:words(External0, $=) of 2 -> {"external="++External0, true}; _ -> {External0, false} end, - {Internal, IChanged} = case string:words(Internal0, "=") of + {Internal, IChanged} = case string:words(Internal0, $=) of 2 -> {"internal="++Internal0, true}; _ -> {Internal0, false} end, @@ -934,7 +935,7 @@ nat_map_add([{external, Ext0}, {internal, Int0}], []) -> riak_core_ring_manager:ring_trans(fun riak_repl_ring:add_nat_map/2, {Ext, Int}), text_out("Added a NAT map from External IP ~p to Internal IP ~p~n", [Ext, Int]); Errors -> - error_out("Invalid IPs given: ~s~n", [string:join(", ", Errors)]) + error_out("Invalid IPs given: ~s~n", [string:join(Errors, ", ")]) end; nat_map_add([{internal,_}, {external,_}]=Args, []) -> nat_map_add(lists:sort(Args), []); @@ -965,7 +966,7 @@ parse_ip(Addr, false) when is_list(Addr) -> collect_bad_ips(List) -> [ case BadIP of - {IP, Port} -> string:join(":", [IP, Port]); + {IP, Port} -> string:join([IP, Port], ":"); _ -> BadIP end || {error, {bad_ip, BadIP}} <- List]. @@ -988,7 +989,7 @@ nat_map_delete([{external, Ext0}, {internal, Int0}], []) -> riak_core_ring_manager:ring_trans(fun riak_repl_ring:del_nat_map/2, {Ext, Int}), text_out("Deleted a NAT map from External IP ~p to Internal IP ~p~n", [Ext, Int]); Errors -> - error_out("Invalid IPs given: ~s~n", [string:join(", ", Errors)]) + error_out("Invalid IPs given: ~s~n", [string:join(Errors, ", ")]) end; nat_map_delete([{internal,_}, {external,_}]=Args, []) -> nat_map_delete(lists:sort(Args), []); diff --git a/tools.mk b/tools.mk index eecfbe4f..ab056496 100644 --- a/tools.mk +++ b/tools.mk @@ -1,8 +1,49 @@ +# ------------------------------------------------------------------- +# +# Copyright (c) 2014 Basho Technologies, Inc. +# +# This file is provided to you under the Apache License, +# Version 2.0 (the "License"); you may not use this file +# except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# ------------------------------------------------------------------- + +# ------------------------------------------------------------------- +# NOTE: This file is is from https://github.com/basho/tools.mk. +# It should not be edited in a project. It should simply be updated +# wholesale when a new version of tools.mk is released. +# ------------------------------------------------------------------- + REBAR ?= ./rebar +REVISION ?= $(shell git rev-parse --short HEAD) +PROJECT ?= $(shell basename `find src -name "*.app.src"` .app.src) + +.PHONY: compile-no-deps test docs xref dialyzer-run dialyzer-quick dialyzer \ + cleanplt upload-docs + +compile-no-deps: + ${REBAR} compile skip_deps=true test: compile ${REBAR} eunit skip_deps=true +upload-docs: docs + @if [ -z "${BUCKET}" -o -z "${PROJECT}" -o -z "${REVISION}" ]; then \ + echo "Set BUCKET, PROJECT, and REVISION env vars to upload docs"; \ + exit 1; fi + @cd doc; s3cmd put -P * "s3://${BUCKET}/${PROJECT}/${REVISION}/" > /dev/null + @echo "Docs built at: http://${BUCKET}.s3-website-us-east-1.amazonaws.com/${PROJECT}/${REVISION}" + docs: ${REBAR} doc skip_deps=true @@ -31,8 +72,31 @@ ${LOCAL_PLT}: compile fi \ fi -dialyzer: ${PLT} ${LOCAL_PLT} +dialyzer-run: @echo "==> $(shell basename $(shell pwd)) (dialyzer)" +# The bulk of the code below deals with the dialyzer.ignore-warnings file +# which contains strings to ignore if output by dialyzer. +# Typically the strings include line numbers. Using them exactly is hard +# to maintain as the code changes. This approach instead ignores the line +# numbers, but takes into account the number of times a string is listed +# for a given file. So if one string is listed once, for example, and it +# appears twice in the warnings, the user is alerted. It is possible but +# unlikely that this approach could mask a warning if one ignored warning +# is removed and two warnings of the same kind appear in the file, for +# example. But it is a trade-off that seems worth it. +# Details of the cryptic commands: +# - Remove line numbers from dialyzer.ignore-warnings +# - Pre-pend duplicate count to each warning with sort | uniq -c +# - Remove annoying white space around duplicate count +# - Save in dialyer.ignore-warnings.tmp +# - Do the same to dialyzer_warnings +# - Remove matches from dialyzer.ignore-warnings.tmp from output +# - Remove duplicate count +# - Escape regex special chars to use lines as regex patterns +# - Add pattern to match any line number (file.erl:\d+:) +# - Anchor to match the entire line (^entire line$) +# - Save in dialyzer_unhandled_warnings +# - Output matches for those patterns found in the original warnings @if [ -f $(LOCAL_PLT) ]; then \ PLTS="$(PLT) $(LOCAL_PLT)"; \ else \ @@ -44,13 +108,37 @@ dialyzer: ${PLT} ${LOCAL_PLT} exit 1; \ fi; \ dialyzer $(DIALYZER_FLAGS) --plts $${PLTS} -c ebin > dialyzer_warnings ; \ - egrep -v "^[[:space:]]*(done|Checking|Proceeding|Compiling)" dialyzer_warnings | grep -F -f dialyzer.ignore-warnings -v > dialyzer_unhandled_warnings ; \ - cat dialyzer_unhandled_warnings ; \ - [ $$(cat dialyzer_unhandled_warnings | wc -l) -eq 0 ] ; \ + cat dialyzer.ignore-warnings \ + | sed -E 's/^([^:]+:)[^:]+:/\1/' \ + | sort \ + | uniq -c \ + | sed -E '/.*\.erl: /!s/^[[:space:]]*[0-9]+[[:space:]]*//' \ + > dialyzer.ignore-warnings.tmp ; \ + egrep -v "^[[:space:]]*(done|Checking|Proceeding|Compiling)" dialyzer_warnings \ + | sed -E 's/^([^:]+:)[^:]+:/\1/' \ + | sort \ + | uniq -c \ + | sed -E '/.*\.erl: /!s/^[[:space:]]*[0-9]+[[:space:]]*//' \ + | grep -F -f dialyzer.ignore-warnings.tmp -v \ + | sed -E 's/^[[:space:]]*[0-9]+[[:space:]]*//' \ + | sed -E 's/([]\^:+?|()*.$${}\[])/\\\1/g' \ + | sed -E 's/(\\\.erl\\\:)/\1\\d+:/g' \ + | sed -E 's/^(.*)$$/^\1$$/g' \ + > dialyzer_unhandled_warnings ; \ + rm dialyzer.ignore-warnings.tmp; \ + if [ $$(cat dialyzer_unhandled_warnings | wc -l) -gt 0 ]; then \ + egrep -f dialyzer_unhandled_warnings dialyzer_warnings ; \ + found_warnings=1; \ + fi; \ + [ "$$found_warnings" != 1 ] ; \ else \ dialyzer $(DIALYZER_FLAGS) --plts $${PLTS} -c ebin; \ fi +dialyzer-quick: compile-no-deps dialyzer-run + +dialyzer: ${PLT} ${LOCAL_PLT} dialyzer-run + cleanplt: @echo @echo "Are you sure? It takes several minutes to re-build." @@ -59,4 +147,3 @@ cleanplt: sleep 5 rm $(PLT) rm $(LOCAL_PLT) - From a157df199f0afb5156fe2555cfe5e7e12efea2d5 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Wed, 11 Mar 2015 10:22:45 -0500 Subject: [PATCH 33/37] Update line numbers and sort lines in ignores file --- dialyzer.ignore-warnings | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/dialyzer.ignore-warnings b/dialyzer.ignore-warnings index 1c1ac711..7c5069f3 100644 --- a/dialyzer.ignore-warnings +++ b/dialyzer.ignore-warnings @@ -1,19 +1,14 @@ -riak_repl_keylist_client.erl:108: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) -riak_repl_keylist_client.erl:218: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) -riak_repl_keylist_client.erl:267: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) -riak_repl_keylist_client.erl:120: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) -riak_repl_keylist_client.erl:132: The call application:set_env('riak_repl',{'progress',_},nonempty_maybe_improper_list()) breaks the contract (Application,Par,Val) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()), is_subtype(Val,term()) -riak_core_connection.erl:108: Function exchange_handshakes_with/4 has no local return -riak_core_connection.erl:172: The call ranch_tcp:send(Socket::port(),Hello::binary()) breaks the contract (inet:socket(),iolist()) -> 'ok' | {'error',atom()} -riak_repl_keylist_client.erl:106: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) -riak_repl_keylist_client.erl:216: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) -riak_repl_keylist_client.erl:265: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) -riak_repl_keylist_client.erl:118: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) -riak_repl_keylist_client.erl:130: The call application:set_env('riak_repl',{'progress',_},nonempty_maybe_improper_list()) breaks the contract (Application,Par,Val) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()), is_subtype(Val,term()) -riak_core_connection_mgr.erl:546: The pattern 'ok' can never match the type {'error',atom()} +Unknown functions cluster_info:format/3 cluster_info:register_app/1 -Unknown functions +gen_leader.erl:1115: The call sys:handle_debug(Debug::any(),{'gen_leader', 'print_event'},any(),Event::{'$leader_cast',_} | {'noreply',_} | {'ok',_} | {'out',_,_,_}) breaks the contract (Debug,FormFunc,Extra,Event) -> [dbg_opt()] when is_subtype(Debug,[dbg_opt()]), is_subtype(FormFunc,dbg_fun()), is_subtype(Extra,term()), is_subtype(Event,system_event()) gen_leader.erl:936: The call sys:handle_debug(Debug::any(),{'gen_leader', 'print_event'},any(),{'in',_}) breaks the contract (Debug,FormFunc,Extra,Event) -> [dbg_opt()] when is_subtype(Debug,[dbg_opt()]), is_subtype(FormFunc,dbg_fun()), is_subtype(Extra,term()), is_subtype(Event,system_event()) gen_leader.erl:952: Function system_terminate/4 has no local return -gen_leader.erl:1115: The call sys:handle_debug(Debug::any(),{'gen_leader', 'print_event'},any(),Event::{'$leader_cast',_} | {'noreply',_} | {'ok',_} | {'out',_,_,_}) breaks the contract (Debug,FormFunc,Extra,Event) -> [dbg_opt()] when is_subtype(Debug,[dbg_opt()]), is_subtype(FormFunc,dbg_fun()), is_subtype(Extra,term()), is_subtype(Event,system_event()) +riak_core_connection.erl:108: Function exchange_handshakes_with/4 has no local return +riak_core_connection.erl:172: The call ranch_tcp:send(Socket::port(),Hello::binary()) breaks the contract (inet:socket(),iolist()) -> 'ok' | {'error',atom()} +riak_core_connection_mgr.erl:546: The pattern 'ok' can never match the type {'error',atom()} +riak_repl_keylist_client.erl:108: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) +riak_repl_keylist_client.erl:120: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) +riak_repl_keylist_client.erl:132: The call application:set_env('riak_repl',{'progress',_},nonempty_maybe_improper_list()) breaks the contract (Application,Par,Val) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()), is_subtype(Val,term()) +riak_repl_keylist_client.erl:218: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) +riak_repl_keylist_client.erl:267: The call application:unset_env('riak_repl',{'progress',_}) breaks the contract (Application,Par) -> 'ok' when is_subtype(Application,atom()), is_subtype(Par,atom()) From 1ee90190ab6d25b1dc1a90e52370ed9c69591da7 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Thu, 12 Mar 2015 09:47:41 -0500 Subject: [PATCH 34/37] Upgrade "modes" command and move helper functions to main console module --- src/riak_repl_console.erl | 58 ++++++++++++++++++++++++++++++++++++- src/riak_repl_console13.erl | 57 ++++++++++++------------------------ 2 files changed, 75 insertions(+), 40 deletions(-) diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index 6f3dec72..1734c49e 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -40,6 +40,12 @@ %% Ring utilities -export([get_ring/0, maybe_set_ring/2]). +%% Clique output utilities +-export([upgrade_warning/3, + output/1, + text_out/1, + text_out/2, + error_out/2]). -spec register_cli() -> ok. register_cli() -> @@ -122,12 +128,32 @@ command([Script|Args]) -> %% printed), try to "upgrade" the arguments to clique-style, %% then invoke clique. nomatch -> - NewCmd = riak_repl_console13:upgrade(Args), + NewCmd = upgrade(riak_repl_console13:upgrade(Args)), clique:run(["riak-repl"|NewCmd]); OkOrError -> OkOrError end. +upgrade(["modes"|[_|_]=Modes]=Args) -> + case upgrade_modes(Modes) of + Modes -> + Args; + NewModes -> + upgrade_warning(Args, "Use `modes set ~s`", [string:join(NewModes, " ")]), + ["modes", "set"|NewModes] + end; +upgrade(Args) -> + Args. + +upgrade_modes(Modes) -> + lists:filtermap(fun upgrade_mode/1, Modes). + +upgrade_mode("mode_repl13") -> {true, "v3=on"}; +upgrade_mode("mode_repl12") -> {true, "v2=on"}; +upgrade_mode("v3="++_=Mode) -> {true, Mode}; +upgrade_mode("v2="++_=Mode) -> {true, Mode}; +upgrade_mode(_) -> false. + %%----------------------- %% Command: status %%----------------------- @@ -589,3 +615,33 @@ simple_parse(Str) -> {ok, AbsForm} = erl_parse:parse_exprs(Tokens), {value, Value, _Bs} = erl_eval:exprs(AbsForm, erl_eval:new_bindings()), Value. + + +%% @doc Registers a warning about using a deprecated form of a +%% command. +upgrade_warning(Args, Fmt, FArgs) -> + put(upgrade_warning, {string:join(Args, " "), io_lib:format(Fmt, FArgs)}). + +output(CmdOut) -> + case get(upgrade_warning) of + undefined -> CmdOut; + {Arguments, Message} -> + erase(upgrade_warning), + [error_msg("The command form `~s` is deprecated. ~s~n", [Arguments, Message]), + CmdOut] + end. + +error_out(Fmt, Args) -> + output(error_msg(Fmt, Args)). + +error_msg(Fmt, Args) -> + [clique_status:alert(text_msg(Fmt, Args))]. + +text_out(Str) -> + text_out(Str, []). + +text_out(Str, Args) -> + output(text_msg(Str, Args)). + +text_msg(Fmt, Args) -> + [clique_status:text(io_lib:format(Fmt, Args))]. diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index afb5290f..66a23a6c 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -4,12 +4,20 @@ -include("riak_repl.hrl"). -export([register_cli/0, commands_usage/0, upgrade/1]). --import(riak_repl_console, [register_command/4, script_name/0]). - --import(clique_status, [text/1, alert/1, table/1]). +-import(clique_status, [text/1, table/1]). -export([cluster_mgr_stats/0]). +-import(riak_repl_console, + [register_command/4, + script_name/0, + get_ring/0, + output/1, + text_out/1, + text_out/2, + error_out/2, + upgrade_warning/3]). + %%----------------------- %% Interface %%----------------------- @@ -410,39 +418,10 @@ upgrade(["nat-map", "del"|Rest]) -> upgrade(Args) -> Args. -%% @doc Registers a warning about using a deprecated form of a -%% command. -upgrade_warning(Args, Fmt, FArgs) -> - put(upgrade_warning, {string:join(Args, " "), io_lib:format(Fmt, FArgs)}). - config_key_translation("max_fssource_node") -> "mdc.fullsync.source.max_workers_per_node"; config_key_translation("max_fssource_cluster") -> "mdc.fullsync.source.max_workers_per_cluster"; config_key_translation("max_fssink_node") -> "mdc.fullsync.sink.max_workers_per_node". -output(CmdOut) -> - case get(upgrade_warning) of - undefined -> CmdOut; - {Arguments, Message} -> - erase(upgrade_warning), - [error_msg("The command form `~s` is deprecated. ~s~n", [Arguments, Message]), - CmdOut] - end. - -error_out(Fmt, Args) -> - output(error_msg(Fmt, Args)). - -error_msg(Fmt, Args) -> - [alert(text_msg(Fmt, Args))]. - -text_out(Str) -> - text_out(Str, []). - -text_out(Str, Args) -> - output(text_msg(Str, Args)). - -text_msg(Fmt, Args) -> - [text(io_lib:format(Fmt, Args))]. - %%----------------------- %% Command: clusterstats %%----------------------- @@ -527,11 +506,11 @@ clustername([], [{name, ClusterName}]) -> %%----------------------- clusters([],[]) -> {ok, Clusters} = riak_core_cluster_mgr:get_known_clusters(), - output(text([ begin - {ok,Members} = riak_core_cluster_mgr:get_ipaddrs_of_cluster(ClusterName), - IPs = [string_of_ipaddr(Addr) || Addr <- Members], - io_lib:format("~s: ~p~n", [ClusterName, IPs]) - end || ClusterName <- Clusters])). + output(text([begin + {ok,Members} = riak_core_cluster_mgr:get_ipaddrs_of_cluster(ClusterName), + IPs = [string_of_ipaddr(Addr) || Addr <- Members], + io_lib:format("~s: ~p~n", [ClusterName, IPs]) + end || ClusterName <- Clusters])). %%----------------------- %% Command: connections @@ -605,7 +584,7 @@ connect([{address, {IP, Port}}], []) -> %% but we still want to be able to print to stderr. This %% will require a clique enhancement. error_out("Error: Unable to establish connections until local cluster is named.~n" - "First use ~s clustername --name NAME ~n", [script_name()]); + "First use ~s clustername --name NAME ~n", [script_name()]); _Name -> riak_core_cluster_mgr:add_remote_cluster({IP, Port}), text_out("Connecting to remote cluster at ~p:~p.", [IP, Port]) @@ -898,7 +877,7 @@ proxy_get_redirect_delete(_, _) -> %% Command: nat-map show %%-------------------------- nat_map_show([], []) -> - Ring = riak_repl_console:get_ring(), + Ring = get_ring(), Headers = [{internal, "Internal"}, {external, "External"}], Rows = [ format_nat_map(Int, Ext) || From 39a29c4a0209b0a796ff182aec894eb36f4ce963 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Mon, 16 Mar 2015 16:36:17 -0500 Subject: [PATCH 35/37] Fix a bunch of bugs and missed items * Register the CLI commands on app startup * Ensure the v2 usage information is registered * Wrap/rewrite "modes" usage * Log the command input and upgrade information to debug before executing * Normalize some output locations to avoid clique output function_clause errors * Return 'nomatch' on the empty command in v2 dispatch * Separate 'clustername' commands into 'show' and 'set' for consistency. * Basic cuttlefish datatypes don't allow "lists", this is a feature of cuttlefish_generator. Restricted the broken types and added TODO. * Corrected a lot of usage strings. * Fixed command upgrades that were poorly matching. * Fixed reversed logic in nat-map add|delete upgrade. * In cases where there was some potentially empty output (e.g. clusters, connections, realtime|fullsync start), pattern-matched the empty list so we can output informative text instead of nothing. * Fixed table-headers on connections and nat-map show. * Fixed some case_clause errors resulting from calls to ring_trans. * Used clique:print instead of io:format in callback that sets fullsync limit configs. --- src/riak_repl_app.erl | 3 + src/riak_repl_console.erl | 25 ++-- src/riak_repl_console12.erl | 2 + src/riak_repl_console13.erl | 226 ++++++++++++++++++++++-------------- 4 files changed, 157 insertions(+), 99 deletions(-) diff --git a/src/riak_repl_app.erl b/src/riak_repl_app.erl index 4b536598..4519225e 100644 --- a/src/riak_repl_app.erl +++ b/src/riak_repl_app.erl @@ -110,6 +110,9 @@ start(_Type, _StartArgs) -> ok = riak_repl2_fscoordinator_serv:sync_register_service(), ok = riak_repl2_pg_block_requester:sync_register_service(), + %% register console commands + ok = riak_repl_console:register_cli(), + %% Don't announce service is available until we've %% registered all listeners. riak_core_node_watcher:service_up(riak_repl, Pid), diff --git a/src/riak_repl_console.erl b/src/riak_repl_console.erl index 1734c49e..70c7e67a 100644 --- a/src/riak_repl_console.erl +++ b/src/riak_repl_console.erl @@ -50,6 +50,7 @@ -spec register_cli() -> ok. register_cli() -> ok = riak_repl_console13:register_cli(), + ok = riak_repl_console12:register_usage(), ok = register_commands(), ok = register_usage(). @@ -88,7 +89,9 @@ modes_usage() -> " Subcommands:\n" " show Shows the active replication modes.\n" " set Toggles active replication modes.\n\n" - " When setting modes, omitting the mode name is the same as `off`. New clusters should use `mode_repl13` (Version 3) exclusively. Version 2 replication will be removed in a future release.". + " When setting modes, omitting the mode name is the same as `off`. New\n" + " clusters should use `v3` (previously `mode_repl13`) exclusively. `v2`\n" + " (previously `mode_repl12`) replication will be removed in a future release.". -spec script_name() -> string(). script_name() -> @@ -129,11 +132,17 @@ command([Script|Args]) -> %% then invoke clique. nomatch -> NewCmd = upgrade(riak_repl_console13:upgrade(Args)), - clique:run(["riak-repl"|NewCmd]); + Cmd = ["riak-repl" | NewCmd], + lager:debug("Running riak-repl command: Script: ~p Args: ~p UpgCmd: ~p RunCmd: ~p", [Script, Args, NewCmd, Cmd]), + clique:run(Cmd); OkOrError -> OkOrError end. +upgrade(["modes", "show"]=Args) -> + Args; +upgrade(["modes", "set"|_Rest]=Args) -> + Args; upgrade(["modes"|[_|_]=Modes]=Args) -> case upgrade_modes(Modes) of Modes -> @@ -160,7 +169,7 @@ upgrade_mode(_) -> false. status([], []) -> All = status(), - clique_status:list(lists:filtermap(fun format_counter_stat/1, All)); + [clique_status:list(lists:filtermap(fun format_counter_stat/1, All))]; status(_,_) -> usage. @@ -260,11 +269,9 @@ modes_set([_|_]=InModes, []) -> NewModes = [ M || {M, true} <- InModes ], ?LOG_USER_CMD("Set replication mode(s) to ~p",[NewModes]), set_modes(NewModes), - [clique_status:text(io_lib:format("Set enabled replication modes to: ~s", - [modes_to_string(NewModes)]))]; + text_out("Set enabled replication modes to: ~s",[modes_to_string(NewModes)]); true -> - [clique_status:alert([clique_status:text(io_lib:format("Invalid modes requested: ~p~n", [InvalidModes]))]), - usage] + error_out("Invalid modes requested: ~p~n", [InvalidModes]) end; modes_set(_,_) -> usage. @@ -622,13 +629,13 @@ simple_parse(Str) -> upgrade_warning(Args, Fmt, FArgs) -> put(upgrade_warning, {string:join(Args, " "), io_lib:format(Fmt, FArgs)}). +-spec output(clique_status:status()) -> clique_status:status(). output(CmdOut) -> case get(upgrade_warning) of undefined -> CmdOut; {Arguments, Message} -> erase(upgrade_warning), - [error_msg("The command form `~s` is deprecated. ~s~n", [Arguments, Message]), - CmdOut] + error_msg("The command form `~s` is deprecated. ~s~n", [Arguments, Message]) ++ CmdOut end. error_out(Fmt, Args) -> diff --git a/src/riak_repl_console12.erl b/src/riak_repl_console12.erl index 21061995..b02defe8 100644 --- a/src/riak_repl_console12.erl +++ b/src/riak_repl_console12.erl @@ -33,6 +33,8 @@ %% last thing on the line is `-h' or `--help', `nomatch' is returned %% so that usage can be printed. -spec dispatch([string()]) -> ok | error | nomatch. +dispatch([]) -> + nomatch; dispatch(Cmd) -> case lists:last(Cmd) of "--help" -> nomatch; diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index 66a23a6c..6f4e6ef6 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -36,16 +36,22 @@ register_commands() -> {protocol, [{longname, "protocol"}, {datatype, atom}]}], fun clusterstats/2), - true = register_command(["clustername"], [], - [{name, [{shortname, "n"}, - {longname, "name"}, + true = register_command(["clustername", "show"], + [],[], + fun clustername_show/2), + true = register_command(["clustername", "set"], + [{name, [{longname, "name"}, {datatype, string}]}], - fun clustername/2), + [], + fun clustername_set/2), true = register_command(["clusters"], [], [], fun clusters/2), true = register_command(["connections"], [], [], fun connections/2), true = register_command(["connect"], [{address, [{datatype, ip}]}], [], fun connect/2), - true = register_command(["disconnect"], [{remote, [{datatype, [ip, string]}]}], [], + %% TODO: disconnect should allow a clustername instead of IP, but + %% the "list of datatypes" thing is not part of + %% cuttlefish_datatypes, but of cuttlefish_generator. + true = register_command(["disconnect"], [{remote, [{datatype, ip}]}], [], fun disconnect/2), true = register_command(["realtime", "enable"], [{remote, [{datatype, string}]}], @@ -115,13 +121,16 @@ register_commands() -> true = register_command(["nat-map", "show"], [], [], fun nat_map_show/2), + %% TODO: nat-map add|delete should allow a hostname instead of IP, + %% but the "list of datatypes" thing is not part of + %% cuttlefish_datatypes, but of cuttlefish_generator. true = register_command(["nat-map", "add"], - [{external, [{datatype, [ip, string]}]}, + [{external, [{datatype, ip}]}, {internal, [{datatype, string}]}], [], fun nat_map_add/2), true = register_command(["nat-map", "delete"], - [{external, [{datatype, [ip, string]}]}, + [{external, [{datatype, ip}]}, {internal, [{datatype, string}]}], [], fun nat_map_delete/2), @@ -133,29 +142,27 @@ register_usage(Cmd, Usage) -> register_usage() -> true = register_usage(["clusterstats"], - "clusterstats [ --protocol=PROTO | --host=IP:PORT ]\n\n" - " Displays cluster statistics, optionally filtered by a protocol or host connection.\n\n" - " Options:\n" - " --protocol=PROTO Filters to a protocol where PROTO is one of:\n" - " rt_repl, proxy_get, identity\n" - " --host=IP:PORT Filters to a specific host, identified by IP and PORT"), + "clusterstats [ --protocol=PROTO | --host=IP:PORT ]\n\n" + " Displays cluster statistics, optionally filtered by a protocol or host connection.\n\n" + " Options:\n" + " --protocol=PROTO Filters to a protocol where PROTO is one of:\n" + " rt_repl, proxy_get, identity\n" + " --host=IP:PORT Filters to a specific host, identified by IP and PORT"), true = register_usage(["clustername"], - "clustername [ (-n | --name) NAME ]\n\n" - " Shows or sets the symbolic clustername. Supplying the `-n` option sets the name.\n\n" - " Options:\n" - " -n NAME, --name NAME Sets the symbolic name to NAME"), + "clustername ( show | set name=NAME )\n\n" + " Shows or sets the symbolic clustername."), true = register_usage(["clusters"], - "clusters\n\n" - " Displays information about known clusters."), + "clusters\n\n" + " Displays information about known clusters."), true = register_usage(["connections"], - "connections\n\n" - " Displays a list of current replication connections."), + "connections\n\n" + " Displays a list of current replication connections."), true = register_usage(["connect"], - "connect address=IP:PORT\n\n" - " Connects to a remote cluster."), + "connect address=IP:PORT\n\n" + " Connects to a remote cluster."), true = register_usage(["disconnect"], - "disconnect remote=(IP:PORT | NAME)\n\n" - " Disconnects from a connected remote cluster."), + "disconnect remote=(IP:PORT | NAME)\n\n" + " Disconnects from a connected remote cluster."), true = register_usage(["realtime"], realtime_usage()), true = register_usage(["realtime", "enable"], realtime_enable_disable_usage()), true = register_usage(["realtime", "disable"], realtime_enable_disable_usage()), @@ -169,8 +176,8 @@ register_usage() -> true = register_usage(["fullsync", "stop"], fullsync_start_stop_usage()), true = register_usage(["proxy-get"], proxy_get_usage()), true = register_usage(["proxy-get", "enable"], proxy_get_enable_disable_usage()), - true = register_usage(["proxy-get", "disable"], proxy_get_usage()), - true = register_usage(["proxy-get", "redirect"], proxy_get_usage()), + true = register_usage(["proxy-get", "disable"], proxy_get_enable_disable_usage()), + true = register_usage(["proxy-get", "redirect"], proxy_get_redirect_usage()), true = register_usage(["proxy-get", "redirect", "show"], fun proxy_get_redirect_show_usage/0), true = register_usage(["proxy-get", "redirect", "add"], fun proxy_get_redirect_add_delete_usage/0), true = register_usage(["proxy-get", "redirect", "delete"], fun proxy_get_redirect_add_delete_usage/0), @@ -189,7 +196,7 @@ register_configs() -> "mdc.fullsync.source.max_workers_per_cluster", "mdc.fullsync.sink.max_workers_per_node"], [true, true, true] = [ clique:register_config(cuttlefish_variable:tokenize(Key), - fun set_fullsync_limit/3) || Key <- Keys ], + fun set_fullsync_limit/3) || Key <- Keys ], ok = clique:register_config_whitelist(Keys), ok. @@ -312,12 +319,12 @@ nat_map_add_del_usage() -> " Add or delete a NAT mapping from the given external IP to the given internal" " IP. An optional external port can be supplied.". -upgrade(["clustername", [$-|_]|_]=Args) -> - %% Don't upgrade a call that includes a flag +upgrade(["clustername", Cmd|_]=Args) when Cmd == "show"; Cmd == "set" -> + %% Don't upgrade a call that includes a command Args; upgrade(["clustername", Arg]=Args) -> - upgrade_warning(Args, "Use `clustername --name ~s`", [Arg]), - ["clustername", "-n", Arg]; + upgrade_warning(Args, "Use `clustername set name=~s`", [Arg]), + ["clustername", "set", "name="++Arg]; upgrade(["clusterstats", [$-|_]|_]=Args) -> %% Don't upgrade a call that includes a flag Args; @@ -346,6 +353,11 @@ upgrade(["disconnect", Arg|Rest]=Args) -> upgrade_warning(Args, "Use `disconnect remote=~s`", [Arg]), ["disconnect", "remote="++Arg|Rest] end; +upgrade(["realtime", Command, [$-|_]|_Rest]=Args) when Command == "enable"; + Command == "disable"; + Command == "start"; + Command == "stop" -> + Args; upgrade(["realtime", Command, Arg|Rest]=Args) when Command == "enable"; Command == "disable"; Command == "start"; @@ -357,7 +369,7 @@ upgrade(["realtime", Command, Arg|Rest]=Args) when Command == "enable"; ["realtime", Command, "remote="++Arg|Rest] end; upgrade(["realtime", Command]=Args) when Command == "start"; - Command == "stop" -> + Command == "stop" -> upgrade_warning(Args, "Use `realtime ~s --all`", [Command]), ["realtime", Command, "--all"]; upgrade(["realtime", "cascades", "always"]=Args) -> @@ -366,6 +378,11 @@ upgrade(["realtime", "cascades", "always"]=Args) -> upgrade(["realtime", "cascades", "never"]=Args) -> upgrade_warning(Args, "Use `realtime cascades disable`", []), ["realtime", "cascades", "disable"]; +upgrade(["fullsync", Command, "--all"|_Rest]=Args) when Command == "enable"; + Command == "disable"; + Command == "start"; + Command == "stop" -> + Args; upgrade(["fullsync", Command, Arg|Rest]=Args) when Command == "enable"; Command == "disable"; Command == "start"; @@ -377,7 +394,7 @@ upgrade(["fullsync", Command, Arg|Rest]=Args) when Command == "enable"; ["fullsync", Command, "remote="++Arg|Rest] end; upgrade(["fullsync", Command]=Args) when Command == "start"; - Command == "stop" -> + Command == "stop" -> upgrade_warning(Args, "Use `fullsync ~s --all`", [Command]), ["fullsync", Command, "--all"]; upgrade(["fullsync", Key]=Args) when Key == "max_fssource_node"; @@ -390,18 +407,18 @@ upgrade(["fullsync", Key, Value]=Args) when Key == "max_fssource_node"; Key == "max_fssource_cluster"; Key == "max_fssink_node" -> TKey = config_key_translation(Key), - upgrade_warning(Args, "Use `show ~s`", [TKey]), + upgrade_warning(Args, "Use `set ~s`", [TKey]), ["set", TKey++"="++Value]; upgrade(["nat-map", Command, External0, Internal0]=Args0) when Command == "add"; - Command == "delete" -> + Command == "delete" -> {External, EChanged} = case string:words(External0, $=) of - 2 -> {"external="++External0, true}; - _ -> {External0, false} + 2 -> {External0, false}; + _ -> {"external="++External0, true} end, {Internal, IChanged} = case string:words(Internal0, $=) of - 2 -> {"internal="++Internal0, true}; - _ -> {Internal0, false} + 2 -> {Internal0, false}; + _ -> {"internal="++Internal0, true} end, Args = [Command, External, Internal], if EChanged orelse IChanged -> @@ -492,25 +509,39 @@ cluster_mgr_stats() -> %% end. %%----------------------- -%% Command: clustername +%% Command: clustername show %%----------------------- -clustername([], []) -> +clustername_show([], []) -> text_out("Cluster name: ~s~n", [riak_core_connection:symbolic_clustername()]); -clustername([], [{name, ClusterName}]) -> +clustername_show(_,_) -> + usage. + +%%----------------------- +%% Command: clustername set +%%----------------------- +clustername_set([{name, ClusterName}], []) -> riak_core_ring_manager:ring_trans(fun riak_core_connection:set_symbolic_clustername/2, ClusterName), - text_out("Cluster name was set to: ~s~n", [ClusterName]). + text_out("Cluster name was set to: ~s", [ClusterName]); +clustername_set(_,_) -> + usage. %%----------------------- %% Command: clusters %%----------------------- clusters([],[]) -> - {ok, Clusters} = riak_core_cluster_mgr:get_known_clusters(), - output(text([begin - {ok,Members} = riak_core_cluster_mgr:get_ipaddrs_of_cluster(ClusterName), - IPs = [string_of_ipaddr(Addr) || Addr <- Members], - io_lib:format("~s: ~p~n", [ClusterName, IPs]) - end || ClusterName <- Clusters])). + case riak_core_cluster_mgr:get_known_clusters() of + {ok, []} -> + text_out("There are no known remote clusters."); + {ok, Clusters} -> + text_out([begin + {ok,Members} = riak_core_cluster_mgr:get_ipaddrs_of_cluster(ClusterName), + IPs = [string_of_ipaddr(Addr) || Addr <- Members], + io_lib:format("~s: ~p~n", [ClusterName, IPs]) + end || ClusterName <- Clusters]) + end; +clusters(_,_) -> + usage. %%----------------------- %% Command: connections @@ -518,14 +549,13 @@ clusters([],[]) -> connections([], []) -> %% get cluster manager's outbound connections to other "remote" clusters, %% which for now, are all the "sinks". - {ok, Conns} = riak_core_cluster_mgr:get_connections(), - Headers = [{connection, "Connection"}, - {cluster_name, "Cluster Name"}, - {pid, "Ctrl-Pid"}, - {members, "Members"}, - {status, "Status"}], - Rows = [format_cluster_conn(Conn) || Conn <- Conns], - output(table([Headers|Rows])). + case riak_core_cluster_mgr:get_connections() of + {ok, []} -> + text_out("There are no connected sink clusters."); + {ok, Conns} -> + Rows = [format_cluster_conn(Conn) || Conn <- Conns], + output([table(Rows)]) + end. string_of_ipaddr({IP, Port}) -> lists:flatten(io_lib:format("~s:~p", [IP, Port])). @@ -544,11 +574,11 @@ string_of_remote({cluster_by_name, ClusterName}) -> %% Remote :: {ip,port} | ClusterName format_cluster_conn({Remote,Pid}) -> {ClusterName, MemberList, Status} = get_cluster_conn_status(Remote, Pid), - [{connection, string_of_remote(Remote)}, - {cluster_name, ClusterName}, - {pid, io_lib:format("~p", [Pid])}, - {members, format_cluster_conn_members(MemberList)}, - {status, format_cluster_conn_status(Status)}]. + [{"Connection", string_of_remote(Remote)}, + {"Cluster Name", ClusterName}, + {"Ctrl-Pid", io_lib:format("~p", [Pid])}, + {"Members", format_cluster_conn_members(MemberList)}, + {"Status", format_cluster_conn_status(Status)}]. get_cluster_conn_status(Remote, Pid) -> %% try to get status from Pid of cluster control channel. if we @@ -584,7 +614,7 @@ connect([{address, {IP, Port}}], []) -> %% but we still want to be able to print to stderr. This %% will require a clique enhancement. error_out("Error: Unable to establish connections until local cluster is named.~n" - "First use ~s clustername --name NAME ~n", [script_name()]); + "First use ~s clustername set name=NAME", [script_name()]); _Name -> riak_core_cluster_mgr:add_remote_cluster({IP, Port}), text_out("Connecting to remote cluster at ~p:~p.", [IP, Port]) @@ -617,7 +647,9 @@ realtime_enable([{remote, Remote}], []) -> case riak_repl2_rt:enable(Remote) of not_changed -> error_out("Realtime replication to cluster ~p already enabled!~n", [Remote]); - {ok, _} -> + {not_changed, _} -> + error_out("Realtime replication to cluster ~p already enabled!~n", [Remote]); + ok -> text_out("Realtime replication to cluster ~p enabled.~n", [Remote]) end; realtime_enable(_, _) -> @@ -631,7 +663,9 @@ realtime_disable([{remote, Remote}], []) -> case riak_repl2_rt:disable(Remote) of not_changed -> error_out("Realtime replication to cluster ~p already disabled!~n", [Remote]); - {ok, _} -> + {not_changed, _} -> + error_out("Realtime replication to cluster ~p already disabled!~n", [Remote]); + ok -> text_out("Realtime replication to cluster ~p disabled.~n", [Remote]) end; realtime_disable(_, _) -> @@ -645,13 +679,19 @@ realtime_start([{remote, Remote}], []) -> case riak_repl2_rt:start(Remote) of not_changed -> error_out("Realtime replication to cluster ~p is already started or not enabled!~n", [Remote]); - {ok, _} -> + {not_changed, _} -> + error_out("Realtime replication to cluster ~p is already started or not enabled!~n", [Remote]); + ok -> text_out("Realtime replication to cluster ~p started.~n", [Remote]) end; realtime_start([], [{all, _}]) -> ?LOG_USER_CMD("Start Realtime Replication to all connected clusters", []), - Remotes = riak_repl2_rt:enabled(), - [ realtime_start([{remote, Remote}], []) || Remote <- Remotes ]; + case riak_repl2_rt:enabled() of + [] -> + error_out("No remote clusters have realtime replication enabled.", []); + Remotes -> + lists:flatten([ realtime_start([{remote, Remote}], []) || Remote <- Remotes ]) + end; realtime_start(_, _) -> usage. @@ -663,13 +703,19 @@ realtime_stop([{remote, Remote}], []) -> case riak_repl2_rt:stop(Remote) of not_changed -> error_out("Realtime replication to cluster ~p is already stopped or not enabled!~n", [Remote]); - {ok, _} -> + {not_changed, _} -> + error_out("Realtime replication to cluster ~p is already stopped or not enabled!~n", [Remote]); + ok -> text_out("Realtime replication to cluster ~p stopped.~n", [Remote]) end; realtime_stop([], [{all, _}]) -> ?LOG_USER_CMD("Stop Realtime Replication to all connected clusters", []), - Remotes = riak_repl2_rt:enabled(), - [ realtime_stop([{remote, Remote}], []) || Remote <- Remotes ]; + case riak_repl2_rt:enabled() of + [] -> + error_out("No remote clusters have realtime replication enabled.", []); + Remotes -> + lists:flatten([ realtime_stop([{remote, Remote}], []) || Remote <- Remotes ]) + end; realtime_stop(_, _) -> usage. @@ -687,7 +733,6 @@ realtime_cascades_enable(_,_) -> %%-------------------------- %% Command: realtime cascades disable %%-------------------------- - realtime_cascades_disable([], []) -> ?LOG_USER_CMD("Disable Realtime Replication cascading", []), riak_core_ring_manager:ring_trans(fun riak_repl_ring:rt_cascades_trans/2, @@ -699,7 +744,6 @@ realtime_cascades_disable(_,_) -> %%-------------------------- %% Command: realtime cascades show %%-------------------------- - realtime_cascades_show([], []) -> case app_helper:get_env(riak_repl, realtime_cascades, always) of always -> @@ -714,12 +758,10 @@ realtime_cascades_show(_, _) -> %%-------------------------- %% Command: fullsync enable %%-------------------------- - fullsync_enable([{remote, Remote}], []) -> Leader = riak_core_cluster_mgr:get_leader(), ?LOG_USER_CMD("Enable Fullsync Replication to cluster ~p", [Remote]), - riak_core_ring_manager:ring_trans(fun - riak_repl_ring:fs_enable_trans/2, Remote), + riak_core_ring_manager:ring_trans(fun riak_repl_ring:fs_enable_trans/2, Remote), _ = riak_repl2_fscoordinator_sup:start_coord(Leader, Remote), text_out("Fullsync replication to cluster ~p enabled.", [Remote]); fullsync_enable(_, _) -> @@ -765,7 +807,7 @@ fullsync_start([], [{all,_}]) -> ?LOG_USER_CMD("Start Fullsync Replication to all connected clusters",[]), _ = [riak_repl2_fscoordinator:start_fullsync(Pid) || {_, Pid} <- Fullsyncs], - text("Fullsync replication started to all connected clusters."); + text_out("Fullsync replication started to all connected clusters."); fullsync_start(_, _) -> usage. @@ -791,7 +833,7 @@ fullsync_stop([], [{all,_}]) -> ?LOG_USER_CMD("Stop Fullsync Replication to all connected clusters",[]), _ = [riak_repl2_fscoordinator:stop_fullsync(Pid) || {_, Pid} <- Fullsyncs], - text("Fullsync replication stopped to all connected clusters."); + text_out("Fullsync replication stopped to all connected clusters."); fullsync_stop(_, _) -> usage. @@ -828,7 +870,7 @@ proxy_get_redirect_cluster_id([], []) -> {ok, Ring} = riak_core_ring_manager:get_my_ring(), ClusterId = lists:flatten( io_lib:format("~p", [riak_core_ring:cluster_name(Ring)])), - text_out("local cluster id: ~p~n", [ClusterId]); + text_out("Local cluster id: ~p", [ClusterId]); proxy_get_redirect_cluster_id(_, _) -> usage. @@ -878,17 +920,20 @@ proxy_get_redirect_delete(_, _) -> %%-------------------------- nat_map_show([], []) -> Ring = get_ring(), - Headers = [{internal, "Internal"}, - {external, "External"}], - Rows = [ format_nat_map(Int, Ext) || - {Int, Ext} <- riak_repl_ring:get_nat_map(Ring)], - output([text("NAT mappings:\n"), table([Headers|Rows])]); + case riak_repl_ring:get_nat_map(Ring) of + [] -> + text_out("No NAT mappings registered.\n"); + Pairs -> + %% Headers = [{internal, "Internal"}, {external, "External"}], + Rows = [ format_nat_map(Int, Ext) || {Int, Ext} <- Pairs ], + output([text("NAT mappings:\n"), table(Rows)]) + end; nat_map_show(_,_) -> usage. format_nat_map(Int, Ext) -> - [{internal, io_lib:format("~s", [print_ip_and_maybe_port(Int)])}, - {external, io_lib:format("~s", [print_ip_and_maybe_port(Ext)])}]. + [{"Internal", io_lib:format("~s", [print_ip_and_maybe_port(Int)])}, + {"External", io_lib:format("~s", [print_ip_and_maybe_port(Ext)])}]. print_ip_and_maybe_port({IP, Port}) when is_tuple(IP) -> [inet_parse:ntoa(IP), $:, integer_to_list(Port)]; @@ -987,7 +1032,7 @@ nat_map_delete(_,_) -> %% happens when a machine bounces and becomes leader? It won't know %% the new value. Seems like we need a central place to hold these %% configuration values. -set_fullsync_limit(["mdc", "fullsync"|Key], Value, _Flags) -> +set_fullsync_limit(["mdc", "fullsync"|Key]=Config, Value, _Flags) -> %% NB: All config settings are done cluster-wide, there's not %% flags for specific nodes like in handoff. AppEnvKey = max_fs_config_key(Key), @@ -998,9 +1043,10 @@ set_fullsync_limit(["mdc", "fullsync"|Key], Value, _Flags) -> [Message, Value]], ?CONSOLE_RPC_TIMEOUT), riak_core_util:rpc_every_member(application, set_env, - [riak_repl, max_fssource_node, Value], + [riak_repl, AppEnvKey, Value], ?CONSOLE_RPC_TIMEOUT), - io:format("Set max number of fullsync workers ~s to ~p~n", [Message, Value]). + Output = text_out("Set max number of fullsync workers ~s to ~p~n", [Message, Value]), + clique:print(Output, ["set", cuttlefish_variable:format(Config)]). max_fs_message(max_fssource_node) -> "per source node"; max_fs_message(max_fssource_cluster) -> "for source cluster"; From 030d1477bb882c97be6eb93d2847d0d874a5b943 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Wed, 18 Mar 2015 09:38:32 -0500 Subject: [PATCH 36/37] Fix config documentation to address review comments --- priv/riak_repl.schema | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/priv/riak_repl.schema b/priv/riak_repl.schema index c285858c..3babeb58 100644 --- a/priv/riak_repl.schema +++ b/priv/riak_repl.schema @@ -60,7 +60,7 @@ ]}. %% @doc Whether to initiate a fullsync on initial connection from a -%% sink cluster. +%% source cluster. {mapping, "mdc.fullsync.start_on_connect", "riak_repl.fullsync_on_connect", [ {datatype, flag}, {default, on} @@ -110,11 +110,12 @@ end end}. -%% @doc By default, fullsync replication will try to coordinate with -%% other Riak subsystems that may be contending for the same -%% resources. This will help to prevent system response degradation -%% under times of heavy load from multiple background tasks. To -%% disable background coordination, set this parameter to off. +%% @doc By default, fullsync replication will not try to coordinate +%% with other Riak subsystems that may be contending for the same +%% resources. Enabling this can help to prevent system response +%% degradation under times of heavy load from multiple background +%% tasks. To disable background coordination, set this parameter to +%% off. {mapping, "mdc.fullsync.background_manager", "riak_repl.fullsync_use_background_manager", [ {datatype, flag}, {default, off}, @@ -131,7 +132,7 @@ ]}. %% @doc The maximum size the realtime replication queue can grow to -%% before new objects are dropped. +%% before old objects are dropped. {mapping, "mdc.realtime.queue_max_bytes", "riak_repl.rtq_max_bytes", [ {datatype, bytesize}, {default, "100MB"} From 6894c2f5761b186772eaedc4a32e28785010ac65 Mon Sep 17 00:00:00 2001 From: Sean Cribbs Date: Fri, 27 Mar 2015 15:53:27 -0700 Subject: [PATCH 37/37] Make upgrades stricter --- src/riak_repl_console13.erl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/riak_repl_console13.erl b/src/riak_repl_console13.erl index 6f4e6ef6..a8ea2573 100644 --- a/src/riak_repl_console13.erl +++ b/src/riak_repl_console13.erl @@ -338,27 +338,27 @@ upgrade(["clusterstats", Arg]=Args) -> ["clusterstats", "--host", Arg]; _ -> Args end; -upgrade(["connect", Arg|Rest]=Args) -> +upgrade(["connect", Arg]=Args) -> case string:words(Arg, $=) of 2 -> Args; 1 -> upgrade_warning(Args, "Use `connect address=~s`", [Arg]), - ["connect", "address="++Arg|Rest]; + ["connect", "address="++Arg]; _ -> Args end; -upgrade(["disconnect", Arg|Rest]=Args) -> +upgrade(["disconnect", Arg]=Args) -> case string:words(Arg, $=) of 2 -> Args; 1 -> upgrade_warning(Args, "Use `disconnect remote=~s`", [Arg]), - ["disconnect", "remote="++Arg|Rest] + ["disconnect", "remote="++Arg] end; upgrade(["realtime", Command, [$-|_]|_Rest]=Args) when Command == "enable"; Command == "disable"; Command == "start"; Command == "stop" -> Args; -upgrade(["realtime", Command, Arg|Rest]=Args) when Command == "enable"; +upgrade(["realtime", Command, Arg]=Args) when Command == "enable"; Command == "disable"; Command == "start"; Command == "stop" -> @@ -366,7 +366,7 @@ upgrade(["realtime", Command, Arg|Rest]=Args) when Command == "enable"; 2 -> Args; 1 -> upgrade_warning(Args, "Use `realtime ~s remote=~s`", [Command, Arg]), - ["realtime", Command, "remote="++Arg|Rest] + ["realtime", Command, "remote="++Arg] end; upgrade(["realtime", Command]=Args) when Command == "start"; Command == "stop" -> @@ -383,7 +383,7 @@ upgrade(["fullsync", Command, "--all"|_Rest]=Args) when Command == "enable"; Command == "start"; Command == "stop" -> Args; -upgrade(["fullsync", Command, Arg|Rest]=Args) when Command == "enable"; +upgrade(["fullsync", Command, Arg]=Args) when Command == "enable"; Command == "disable"; Command == "start"; Command == "stop" -> @@ -391,7 +391,7 @@ upgrade(["fullsync", Command, Arg|Rest]=Args) when Command == "enable"; 2 -> Args; 1 -> upgrade_warning(Args, "Use `fullsync ~s remote=~s`", [Command, Arg]), - ["fullsync", Command, "remote="++Arg|Rest] + ["fullsync", Command, "remote="++Arg] end; upgrade(["fullsync", Command]=Args) when Command == "start"; Command == "stop" ->