diff options
| author | Michael Klishin <mklishin@pivotal.io> | 2016-10-14 07:32:40 +0800 |
|---|---|---|
| committer | Michael Klishin <mklishin@pivotal.io> | 2016-10-14 07:32:40 +0800 |
| commit | 0454457f4ef429d6b9d0337aa43fa738da3b5d72 (patch) | |
| tree | c6c8acb02f6e9b1dfc017ad90782f4b56f948ab6 /src | |
| parent | 45c728a4a5b637521a17f0472ed29a2a4187763a (diff) | |
| parent | fdd9f295d8b33a0774b3fa1f9753ff044fcfee15 (diff) | |
| download | rabbitmq-server-git-0454457f4ef429d6b9d0337aa43fa738da3b5d72.tar.gz | |
Merge branch 'master' into rabbitmq-server-486
Conflicts:
src/rabbit.app.src
Diffstat (limited to 'src')
| -rw-r--r-- | src/rabbit.app.src | 7 | ||||
| -rw-r--r-- | src/rabbit.erl | 106 | ||||
| -rw-r--r-- | src/rabbit_control_main.erl | 16 | ||||
| -rw-r--r-- | src/rabbit_control_pbe.erl | 79 | ||||
| -rw-r--r-- | src/rabbit_mirror_queue_mode_nodes.erl | 36 | ||||
| -rw-r--r-- | src/rabbit_pbe.erl | 194 | ||||
| -rw-r--r-- | src/rabbit_vhost_limit.erl | 37 |
7 files changed, 456 insertions, 19 deletions
diff --git a/src/rabbit.app.src b/src/rabbit.app.src index 87401662c8..b22ef7ae5b 100644 --- a/src/rabbit.app.src +++ b/src/rabbit.app.src @@ -108,6 +108,13 @@ %% used by rabbit_peer_discovery_classic_config {cluster_nodes, {[], disc}}, + {decoder_config, [ + {cipher, aes_cbc256}, + {hash, sha512}, + {iterations, 1000}, + {passphrase, undefined} + ]}, + %% rabbitmq-server-973 {lazy_queue_explicit_gc_run_operation_threshold, 250} ]}]}. diff --git a/src/rabbit.erl b/src/rabbit.erl index 5db2c40b66..089fc0dd1c 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -24,7 +24,7 @@ start_fhc/0]). -export([start/2, stop/1, prep_stop/1]). -export([start_apps/1, stop_apps/1]). --export([log_locations/0, config_files/0]). %% for testing and mgmt-agent +-export([log_locations/0, config_files/0, decrypt_config/2]). %% for testing and mgmt-agent -ifdef(TEST). @@ -465,6 +465,38 @@ stop_and_halt() -> start_apps(Apps) -> app_utils:load_applications(Apps), + + DecoderConfig = case application:get_env(rabbit, decoder_config) of + undefined -> + []; + {ok, Val} -> + Val + end, + PassPhrase = case proplists:get_value(passphrase, DecoderConfig) of + prompt -> + IoDevice = get_input_iodevice(), + io:setopts(IoDevice, [{echo, false}]), + PP = lists:droplast(io:get_line(IoDevice, + "\nPlease enter the passphrase to unlock encrypted " + "configuration entries.\n\nPassphrase: ")), + io:setopts(IoDevice, [{echo, true}]), + io:format(IoDevice, "~n", []), + PP; + {file, Filename} -> + {ok, File} = file:read_file(Filename), + [PP|_] = binary:split(File, [<<"\r\n">>, <<"\n">>]), + PP; + PP -> + PP + end, + Algo = { + proplists:get_value(cipher, DecoderConfig, rabbit_pbe:default_cipher()), + proplists:get_value(hash, DecoderConfig, rabbit_pbe:default_hash()), + proplists:get_value(iterations, DecoderConfig, rabbit_pbe:default_iterations()), + PassPhrase + }, + decrypt_config(Apps, Algo), + OrderedApps = app_utils:app_dependency_order(Apps, false), case lists:member(rabbit, Apps) of false -> rabbit_boot_steps:run_boot_steps(Apps); %% plugin activation @@ -473,6 +505,78 @@ start_apps(Apps) -> ok = app_utils:start_applications(OrderedApps, handle_app_error(could_not_start)). +%% This function retrieves the correct IoDevice for requesting +%% input. The problem with using the default IoDevice is that +%% the Erlang shell prevents us from getting the input. +%% +%% Instead we therefore look for the io process used by the +%% shell and if it can't be found (because the shell is not +%% started e.g with -noshell) we use the 'user' process. +%% +%% This function will not work when either -oldshell or -noinput +%% options are passed to erl. +get_input_iodevice() -> + case whereis(user) of + undefined -> user; + User -> + case group:interfaces(User) of + [] -> + user; + [{user_drv, Drv}] -> + case user_drv:interfaces(Drv) of + [] -> + user; + [{current_group, IoDevice}] -> + IoDevice + end + end + end. + +decrypt_config([], _) -> + ok; +decrypt_config([App|Apps], Algo) -> + decrypt_app(App, application:get_all_env(App), Algo), + decrypt_config(Apps, Algo). + +decrypt_app(_, [], _) -> + ok; +decrypt_app(App, [{Key, Value}|Tail], Algo) -> + try begin + case decrypt(Value, Algo) of + Value -> + ok; + NewValue -> + application:set_env(App, Key, NewValue) + end + end + catch + exit:{bad_configuration, decoder_config} -> + exit({bad_configuration, decoder_config}); + _:Msg -> + rabbit_log:info("Error while decrypting key '~p'. Please check encrypted value, passphrase, and encryption configuration~n", [Key]), + exit({decryption_error, {key, Key}, Msg}) + end, + decrypt_app(App, Tail, Algo). + +decrypt({encrypted, _}, {_, _, _, undefined}) -> + exit({bad_configuration, decoder_config}); +decrypt({encrypted, EncValue}, {Cipher, Hash, Iterations, Password}) -> + rabbit_pbe:decrypt_term(Cipher, Hash, Iterations, Password, EncValue); +decrypt(List, Algo) when is_list(List) -> + decrypt_list(List, Algo, []); +decrypt(Value, _) -> + Value. + +%% We make no distinction between strings and other lists. +%% When we receive a string, we loop through each element +%% and ultimately return the string unmodified, as intended. +decrypt_list([], _, Acc) -> + lists:reverse(Acc); +decrypt_list([{Key, Value}|Tail], Algo, Acc) when Key =/= encrypted -> + decrypt_list(Tail, Algo, [{Key, decrypt(Value, Algo)}|Acc]); +decrypt_list([Value|Tail], Algo, Acc) -> + decrypt_list(Tail, Algo, [decrypt(Value, Algo)|Acc]). + stop_apps(Apps) -> ok = app_utils:stop_applications( Apps, handle_app_error(error_during_shutdown)), diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index f48f0349aa..1619f25494 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -97,7 +97,8 @@ {trace_off, [?VHOST_DEF]}, set_vm_memory_high_watermark, set_disk_free_limit, - help + help, + {encode, [?DECODE_DEF, ?CIPHER_DEF, ?HASH_DEF, ?ITERATIONS_DEF, ?LIST_CIPHERS_DEF, ?LIST_HASHES_DEF]} ]). -define(GLOBAL_QUERIES, @@ -119,7 +120,7 @@ [stop, stop_app, start_app, wait, reset, force_reset, rotate_logs, join_cluster, change_cluster_node_type, update_cluster_nodes, forget_cluster_node, rename_cluster_node, cluster_status, status, - environment, eval, force_boot, help, hipe_compile]). + environment, eval, force_boot, help, hipe_compile, encode]). %% [Command | {Command, DefaultTimeoutInMilliSeconds}] -define(COMMANDS_WITH_TIMEOUT, @@ -613,6 +614,17 @@ action(eval, Node, [Expr], _Opts, _Inform) -> action(help, _Node, _Args, _Opts, _Inform) -> io:format("~s", [rabbit_ctl_usage:usage()]); +action(encode, _Node, Args, Opts, _Inform) -> + ListCiphers = lists:member({?LIST_CIPHERS_OPT, true}, Opts), + ListHashes = lists:member({?LIST_HASHES_OPT, true}, Opts), + Decode = lists:member({?DECODE_OPT, true}, Opts), + Cipher = list_to_atom(proplists:get_value(?CIPHER_OPT, Opts)), + Hash = list_to_atom(proplists:get_value(?HASH_OPT, Opts)), + Iterations = list_to_integer(proplists:get_value(?ITERATIONS_OPT, Opts)), + + {_, Msg} = rabbit_control_pbe:encode(ListCiphers, ListHashes, Decode, Cipher, Hash, Iterations, Args), + io:format(Msg ++ "~n"); + action(Command, Node, Args, Opts, Inform) -> %% For backward compatibility, run commands accepting a timeout with %% the default timeout. diff --git a/src/rabbit_control_pbe.erl b/src/rabbit_control_pbe.erl new file mode 100644 index 0000000000..2fa2c90a6e --- /dev/null +++ b/src/rabbit_control_pbe.erl @@ -0,0 +1,79 @@ +% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (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.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. +%% + +-module(rabbit_control_pbe). + +-export([encode/7]). + +% for testing purposes +-export([evaluate_input_as_term/1]). + +encode(ListCiphers, _ListHashes, _Decode, _Cipher, _Hash, _Iterations, _Args) when ListCiphers -> + {ok, io_lib:format("~p", [rabbit_pbe:supported_ciphers()])}; + +encode(_ListCiphers, ListHashes, _Decode, _Cipher, _Hash, _Iterations, _Args) when ListHashes -> + {ok, io_lib:format("~p", [rabbit_pbe:supported_hashes()])}; + +encode(_ListCiphers, _ListHashes, Decode, Cipher, Hash, Iterations, Args) -> + CipherExists = lists:member(Cipher, rabbit_pbe:supported_ciphers()), + HashExists = lists:member(Hash, rabbit_pbe:supported_hashes()), + encode_encrypt_decrypt(CipherExists, HashExists, Decode, Cipher, Hash, Iterations, Args). + +encode_encrypt_decrypt(CipherExists, _HashExists, _Decode, _Cipher, _Hash, _Iterations, _Args) when CipherExists =:= false -> + {error, io_lib:format("The requested cipher is not supported", [])}; + +encode_encrypt_decrypt(_CipherExists, HashExists, _Decode, _Cipher, _Hash, _Iterations, _Args) when HashExists =:= false -> + {error, io_lib:format("The requested hash is not supported", [])}; + +encode_encrypt_decrypt(_CipherExists, _HashExists, _Decode, _Cipher, _Hash, Iterations, _Args) when Iterations =< 0 -> + {error, io_lib:format("The requested number of iterations is incorrect", [])}; + +encode_encrypt_decrypt(_CipherExists, _HashExists, Decode, Cipher, Hash, Iterations, Args) when length(Args) == 2, Decode =:= false -> + [Value, PassPhrase] = Args, + try begin + TermValue = evaluate_input_as_term(Value), + Result = rabbit_pbe:encrypt_term(Cipher, Hash, Iterations, list_to_binary(PassPhrase), TermValue), + {ok, io_lib:format("~p", [{encrypted, Result}])} + end + catch + _:Msg -> {error, io_lib:format("Error during cipher operation: ~p", [Msg])} + end; + +encode_encrypt_decrypt(_CipherExists, _HashExists, Decode, Cipher, Hash, Iterations, Args) when length(Args) == 2, Decode -> + [Value, PassPhrase] = Args, + try begin + TermValue = evaluate_input_as_term(Value), + TermToDecrypt = case TermValue of + {encrypted, EncryptedTerm} -> + EncryptedTerm; + _ -> + TermValue + end, + Result = rabbit_pbe:decrypt_term(Cipher, Hash, Iterations, list_to_binary(PassPhrase), TermToDecrypt), + {ok, io_lib:format("~p", [Result])} + end + catch + _:Msg -> {error, io_lib:format("Error during cipher operation: ~p", [Msg])} + end; + +encode_encrypt_decrypt(_CipherExists, _HashExists, _Decode, _Cipher, _Hash, _Iterations, _Args) -> + {error, io_lib:format("Please provide a value to encode/decode and a passphrase", [])}. + +evaluate_input_as_term(Input) -> + {ok,Tokens,_EndLine} = erl_scan:string(Input ++ "."), + {ok,AbsForm} = erl_parse:parse_exprs(Tokens), + {value,TermValue,_Bs} = erl_eval:exprs(AbsForm, erl_eval:new_bindings()), + TermValue. diff --git a/src/rabbit_mirror_queue_mode_nodes.erl b/src/rabbit_mirror_queue_mode_nodes.erl index e63f340373..31c55722a5 100644 --- a/src/rabbit_mirror_queue_mode_nodes.erl +++ b/src/rabbit_mirror_queue_mode_nodes.erl @@ -32,29 +32,37 @@ description() -> [{description, <<"Mirror queue to specified nodes">>}]. -suggested_queue_nodes(Nodes0, MNode, _SNodes, SSNodes, Poss) -> - Nodes1 = [list_to_atom(binary_to_list(Node)) || Node <- Nodes0], +suggested_queue_nodes(PolicyNodes0, CurrentMaster, _SNodes, SSNodes, NodesRunningRabbitMQ) -> + PolicyNodes1 = [list_to_atom(binary_to_list(Node)) || Node <- PolicyNodes0], %% If the current master is not in the nodes specified, then what we want %% to do depends on whether there are any synchronised slaves. If there %% are then we can just kill the current master - the admin has asked for %% a migration and we should give it to them. If there are not however %% then we must keep the master around so as not to lose messages. - Nodes = case SSNodes of - [] -> lists:usort([MNode | Nodes1]); - _ -> Nodes1 - end, - Unavailable = Nodes -- Poss, - Available = Nodes -- Unavailable, - case Available of + + PolicyNodes = case SSNodes of + [] -> lists:usort([CurrentMaster | PolicyNodes1]); + _ -> PolicyNodes1 + end, + Unavailable = PolicyNodes -- NodesRunningRabbitMQ, + AvailablePolicyNodes = PolicyNodes -- Unavailable, + case AvailablePolicyNodes of [] -> %% We have never heard of anything? Not much we can do but %% keep the master alive. - {MNode, []}; - _ -> case lists:member(MNode, Available) of - true -> {MNode, Available -- [MNode]}; + {CurrentMaster, []}; + _ -> case lists:member(CurrentMaster, AvailablePolicyNodes) of + true -> {CurrentMaster, + AvailablePolicyNodes -- [CurrentMaster]}; false -> %% Make sure the new master is synced! In order to %% get here SSNodes must not be empty. - [NewMNode | _] = SSNodes, - {NewMNode, Available -- [NewMNode]} + SyncPolicyNodes = [Node || + Node <- AvailablePolicyNodes, + lists:member(Node, SSNodes)], + NewMaster = case SyncPolicyNodes of + [Node | _] -> Node; + [] -> erlang:hd(SSNodes) + end, + {NewMaster, AvailablePolicyNodes -- [NewMaster]} end end. diff --git a/src/rabbit_pbe.erl b/src/rabbit_pbe.erl new file mode 100644 index 0000000000..f4998d4a13 --- /dev/null +++ b/src/rabbit_pbe.erl @@ -0,0 +1,194 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (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.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. +%% + +-module(rabbit_pbe). + +-export([supported_ciphers/0, supported_hashes/0, default_cipher/0, default_hash/0, default_iterations/0]). +-export([encrypt_term/5, decrypt_term/5]). +-export([encrypt/5, decrypt/5]). + +%% Supported ciphers and hashes + +supported_ciphers() -> + proplists:get_value(ciphers, crypto:supports()) + -- [aes_ctr, aes_ecb, des_ecb, blowfish_ecb, rc4, aes_gcm]. + +supported_hashes() -> + proplists:get_value(hashs, crypto:supports()) + -- [md4, ripemd160]. + +%% Default encryption parameters (keep those in sync with rabbit.app.src) +default_cipher() -> + aes_cbc256. + +default_hash() -> + sha512. + +default_iterations() -> + 1000. + +%% Encryption/decryption of arbitrary Erlang terms. + +encrypt_term(Cipher, Hash, Iterations, PassPhrase, Term) -> + encrypt(Cipher, Hash, Iterations, PassPhrase, term_to_binary(Term)). + +decrypt_term(Cipher, Hash, Iterations, PassPhrase, Base64Binary) -> + binary_to_term(decrypt(Cipher, Hash, Iterations, PassPhrase, Base64Binary)). + +%% The cipher for encryption is from the list of supported ciphers. +%% The hash for generating the key from the passphrase is from the list +%% of supported hashes. See crypto:supports/0 to obtain both lists. +%% The key is generated by applying the hash N times with N >= 1. +%% +%% The encrypt/5 function returns a base64 binary and the decrypt/5 +%% function accepts that same base64 binary. + +-spec encrypt(crypto:block_cipher(), crypto:hash_algorithms(), + pos_integer(), iodata(), binary()) -> binary(). +encrypt(Cipher, Hash, Iterations, PassPhrase, ClearText) -> + Salt = crypto:strong_rand_bytes(16), + Ivec = crypto:strong_rand_bytes(iv_length(Cipher)), + Key = make_key(Cipher, Hash, Iterations, PassPhrase, Salt), + Binary = crypto:block_encrypt(Cipher, Key, Ivec, pad(Cipher, ClearText)), + base64:encode(<< Salt/binary, Ivec/binary, Binary/binary >>). + +-spec decrypt(crypto:block_cipher(), crypto:hash_algorithms(), + pos_integer(), iodata(), binary()) -> binary(). +decrypt(Cipher, Hash, Iterations, PassPhrase, Base64Binary) -> + IvLength = iv_length(Cipher), + << Salt:16/binary, Ivec:IvLength/binary, Binary/bits >> = base64:decode(Base64Binary), + Key = make_key(Cipher, Hash, Iterations, PassPhrase, Salt), + unpad(crypto:block_decrypt(Cipher, Key, Ivec, Binary)). + +%% Generate a key from a passphrase. + +make_key(Cipher, Hash, Iterations, PassPhrase, Salt) -> + Key = pbdkdf2(PassPhrase, Salt, Iterations, key_length(Cipher), + fun crypto:hmac/4, Hash, hash_length(Hash)), + if + Cipher =:= des3_cbc; Cipher =:= des3_cbf; Cipher =:= des3_cfb; Cipher =:= des_ede3 -> + << A:8/binary, B:8/binary, C:8/binary >> = Key, + [A, B, C]; + true -> + Key + end. + +%% Functions to pad/unpad input to a multiplier of block size. + +pad(Cipher, Data) -> + BlockSize = block_size(Cipher), + N = BlockSize - (byte_size(Data) rem BlockSize), + Pad = list_to_binary(lists:duplicate(N, N)), + <<Data/binary, Pad/binary>>. + +unpad(Data) -> + N = binary:last(Data), + binary:part(Data, 0, byte_size(Data) - N). + +%% These functions are necessary because the current Erlang crypto interface +%% is lacking interfaces to the following OpenSSL functions: +%% +%% * int EVP_MD_size(const EVP_MD *md); +%% * int EVP_CIPHER_iv_length(const EVP_CIPHER *e); +%% * int EVP_CIPHER_key_length(const EVP_CIPHER *e); +%% * int EVP_CIPHER_block_size(const EVP_CIPHER *e); + +hash_length(md4) -> 16; +hash_length(md5) -> 16; +hash_length(sha) -> 20; +hash_length(sha224) -> 28; +hash_length(sha256) -> 32; +hash_length(sha384) -> 48; +hash_length(sha512) -> 64. + +iv_length(des_cbc) -> 8; +iv_length(des_cfb) -> 8; +iv_length(des3_cbc) -> 8; +iv_length(des3_cbf) -> 8; +iv_length(des3_cfb) -> 8; +iv_length(des_ede3) -> 8; +iv_length(blowfish_cbc) -> 8; +iv_length(blowfish_cfb64) -> 8; +iv_length(blowfish_ofb64) -> 8; +iv_length(rc2_cbc) -> 8; +iv_length(aes_cbc) -> 16; +iv_length(aes_cbc128) -> 16; +iv_length(aes_cfb8) -> 16; +iv_length(aes_cfb128) -> 16; +iv_length(aes_cbc256) -> 16; +iv_length(aes_ige256) -> 32. + +key_length(des_cbc) -> 8; +key_length(des_cfb) -> 8; +key_length(des3_cbc) -> 24; +key_length(des3_cbf) -> 24; +key_length(des3_cfb) -> 24; +key_length(des_ede3) -> 24; +key_length(blowfish_cbc) -> 16; +key_length(blowfish_cfb64) -> 16; +key_length(blowfish_ofb64) -> 16; +key_length(rc2_cbc) -> 16; +key_length(aes_cbc) -> 16; +key_length(aes_cbc128) -> 16; +key_length(aes_cfb8) -> 16; +key_length(aes_cfb128) -> 16; +key_length(aes_cbc256) -> 32; +key_length(aes_ige256) -> 16. + +block_size(aes_cbc256) -> 32; +block_size(aes_cbc128) -> 32; +block_size(aes_ige256) -> 32; +block_size(aes_cbc) -> 32; +block_size(_) -> 8. + +%% The following was taken from OTP's lib/public_key/src/pubkey_pbe.erl +%% +%% This is an undocumented interface to password-based encryption algorithms. +%% These functions have been copied here to stay compatible with R16B03. + +%%-------------------------------------------------------------------- +-spec pbdkdf2(string(), iodata(), integer(), integer(), fun(), atom(), integer()) + -> binary(). +%% +%% Description: Implements password based decryption key derive function 2. +%% Exported mainly for testing purposes. +%%-------------------------------------------------------------------- +pbdkdf2(Password, Salt, Count, DerivedKeyLen, Prf, PrfHash, PrfOutputLen)-> + NumBlocks = ceiling(DerivedKeyLen / PrfOutputLen), + NumLastBlockOctets = DerivedKeyLen - (NumBlocks - 1) * PrfOutputLen , + blocks(NumBlocks, NumLastBlockOctets, 1, Password, Salt, + Count, Prf, PrfHash, PrfOutputLen, <<>>). + +blocks(1, N, Index, Password, Salt, Count, Prf, PrfHash, PrfLen, Acc) -> + <<XorSum:N/binary, _/binary>> = xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen), + <<Acc/binary, XorSum/binary>>; +blocks(NumBlocks, N, Index, Password, Salt, Count, Prf, PrfHash, PrfLen, Acc) -> + XorSum = xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen), + blocks(NumBlocks -1, N, Index +1, Password, Salt, Count, Prf, PrfHash, + PrfLen, <<Acc/binary, XorSum/binary>>). + +xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen) -> + Result = Prf(PrfHash, Password, [Salt,<<Index:32/unsigned-big-integer>>], PrfLen), + do_xor_sum(Prf, PrfHash, PrfLen, Result, Password, Count-1, Result). + +do_xor_sum(_, _, _, _, _, 0, Acc) -> + Acc; +do_xor_sum(Prf, PrfHash, PrfLen, Prev, Password, Count, Acc) -> + Result = Prf(PrfHash, Password, Prev, PrfLen), + do_xor_sum(Prf, PrfHash, PrfLen, Result, Password, Count-1, crypto:exor(Acc, Result)). + +ceiling(Float) -> + erlang:round(Float + 0.5). diff --git a/src/rabbit_vhost_limit.erl b/src/rabbit_vhost_limit.erl index bd79f4dd45..b933c31402 100644 --- a/src/rabbit_vhost_limit.erl +++ b/src/rabbit_vhost_limit.erl @@ -21,12 +21,14 @@ -include("rabbit.hrl"). -export([register/0]). --export([parse_set/2, clear/1]). +-export([parse_set/2, set/2, clear/1]). +-export([list/0, list/1]). +-export([update_limit/3, clear_limit/2, get_limit/2]). -export([validate/5, notify/4, notify_clear/3]). -export([connection_limit/1, queue_limit/1, is_over_queue_limit/1, is_over_connection_limit/1]). --import(rabbit_misc, [pget/2]). +-import(rabbit_misc, [pget/2, pget/3]). -rabbit_boot_step({?MODULE, [{description, "vhost limit parameters"}, @@ -57,6 +59,21 @@ connection_limit(VirtualHost) -> queue_limit(VirtualHost) -> get_limit(VirtualHost, <<"max-queues">>). +-spec list() -> [{rabbit_types:vhost(), rabbit_types:infos()}]. + +list() -> + case rabbit_runtime_parameters:list_component(<<"vhost-limits">>) of + [] -> []; + Params -> [ {pget(vhost, Param), pget(value, Param)} + || Param <- Params, + pget(value, Param) =/= undefined, + pget(name, Param) == <<"limits">> ] + end. + +-spec list(rabbit_types:vhost()) -> rabbit_types:infos(). + +list(VHost) -> + rabbit_runtime_parameters:value(VHost, <<"vhost-limits">>, <<"limits">>, []). -spec is_over_connection_limit(rabbit_types:vhost()) -> {true, non_neg_integer()} | false. @@ -122,6 +139,22 @@ clear(VHost) -> rabbit_runtime_parameters:clear_any(VHost, <<"vhost-limits">>, <<"limits">>). +update_limit(VHost, Name, Value) -> + OldDef = case rabbit_runtime_parameters:list(VHost, <<"vhost-limits">>) of + [] -> []; + [Param] -> pget(value, Param, []) + end, + NewDef = [{Name, Value} | lists:keydelete(Name, 1, OldDef)], + set(VHost, NewDef). + +clear_limit(VHost, Name) -> + OldDef = case rabbit_runtime_parameters:list(VHost, <<"vhost-limits">>) of + [] -> []; + [Param] -> pget(value, Param, []) + end, + NewDef = lists:keydelete(Name, 1, OldDef), + set(VHost, NewDef). + vhost_limit_validation() -> [{<<"max-connections">>, fun rabbit_parameter_validation:integer/2, optional}, {<<"max-queues">>, fun rabbit_parameter_validation:integer/2, optional}]. |
