emissary 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +203 -0
- data/README.txt +54 -0
- data/VERSION.yml +4 -0
- data/bin/emissary +196 -0
- data/bin/emissary-setup +75 -0
- data/etc/emissary/config.ini +13 -0
- data/etc/init.d/emissary +50 -0
- data/lib/emissary.rb +223 -0
- data/lib/emissary/agent.rb +61 -0
- data/lib/emissary/agent/emissary.rb +163 -0
- data/lib/emissary/agent/error.rb +26 -0
- data/lib/emissary/agent/file.rb +26 -0
- data/lib/emissary/agent/gem.rb +42 -0
- data/lib/emissary/agent/mysql.rb +219 -0
- data/lib/emissary/agent/ping.rb +37 -0
- data/lib/emissary/agent/proxy.rb +26 -0
- data/lib/emissary/agent/rabbitmq.rb +233 -0
- data/lib/emissary/agent/sshkeys.rb +152 -0
- data/lib/emissary/agent/stats.rb +96 -0
- data/lib/emissary/agent/test.rb +40 -0
- data/lib/emissary/config.rb +231 -0
- data/lib/emissary/core_ext/blank.rb +60 -0
- data/lib/emissary/core_ext/misc_object.rb +21 -0
- data/lib/emissary/core_ext/symbolize.rb +33 -0
- data/lib/emissary/daemon.rb +404 -0
- data/lib/emissary/errors.rb +106 -0
- data/lib/emissary/gem_helper.rb +183 -0
- data/lib/emissary/identity.rb +183 -0
- data/lib/emissary/identity/ec2.rb +64 -0
- data/lib/emissary/identity/unix.rb +67 -0
- data/lib/emissary/logger.rb +130 -0
- data/lib/emissary/message.rb +217 -0
- data/lib/emissary/operator.rb +274 -0
- data/lib/emissary/operator/amqp.rb +203 -0
- data/lib/emissary/server.rb +98 -0
- data/lib/emissary/servolux.rb +75 -0
- metadata +262 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
# Copyright 2010 The New York Times
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
#
|
15
|
+
#
|
16
|
+
module Emissary
|
17
|
+
class Agent::Error < Agent
|
18
|
+
def valid_methods
|
19
|
+
[ :any ]
|
20
|
+
end
|
21
|
+
|
22
|
+
def activate
|
23
|
+
message
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Copyright 2010 The New York Times
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
#
|
15
|
+
#
|
16
|
+
module Emissary
|
17
|
+
class Agent::File < Agent
|
18
|
+
def valid_methods
|
19
|
+
[ :any ]
|
20
|
+
end
|
21
|
+
|
22
|
+
def activate
|
23
|
+
throw :skip_implicit_response
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Copyright 2010 The New York Times
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
#
|
15
|
+
#
|
16
|
+
|
17
|
+
module Emissary
|
18
|
+
class Agent::Gem < Agent
|
19
|
+
def valid_methods
|
20
|
+
[ :update, :install, :remove, :uninstall, :version ]
|
21
|
+
end
|
22
|
+
|
23
|
+
def version gem_name
|
24
|
+
::Emissary.GemHelper.new(gem_name).version
|
25
|
+
end
|
26
|
+
|
27
|
+
# Updates Emissary from the given source to the given version
|
28
|
+
def install gem_name, version = :latest, source_url = :default
|
29
|
+
::Emissary::GemHelper.new(gem_name).install(version, source_url)
|
30
|
+
end
|
31
|
+
|
32
|
+
def update gem_name, version = :latest, source_url = :default
|
33
|
+
::Emissary::GemHelper.new(gem_name).update(version, source_url)
|
34
|
+
end
|
35
|
+
|
36
|
+
def uninstall gem_name, version = :latest, ignore_dependencies = true, remove_executables = false
|
37
|
+
::Emissary::GemHelper.new(gem_name).uninstall(version, ignore_dependencies, remove_executables)
|
38
|
+
end
|
39
|
+
alias :remove :uninstall
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# Copyright 2010 The New York Times
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
#
|
15
|
+
#
|
16
|
+
require "mysql"
|
17
|
+
require "monitor"
|
18
|
+
require 'timeout'
|
19
|
+
|
20
|
+
module Emissary
|
21
|
+
class Agent::Mysql < Agent
|
22
|
+
DEFAULT_COORDINATES_FILE = '/var/nyt/mysql/master.coordinates'.freeze
|
23
|
+
|
24
|
+
def valid_methods
|
25
|
+
[ :lock, :unlock, :status ]
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_accessor :coordinates_file
|
29
|
+
|
30
|
+
def lock(host, user, password, timeout = Agent::Mysql::Helper::DEFAULT_TIMEOUT, coordinates_file = nil)
|
31
|
+
@coordinates_file ||= coordinates_file
|
32
|
+
@coordinates_file ||= config[:agents][:mysql][:coordinates_file] rescue nil
|
33
|
+
@coordinates_file ||= DEFAULT_COORDINATES_FILE
|
34
|
+
|
35
|
+
locker = ::Emissary::Agent::Mysql::Helper.new(host, user, password, timeout)
|
36
|
+
locker.lock!
|
37
|
+
|
38
|
+
filename, position = locker.get_binlog_info
|
39
|
+
|
40
|
+
unless filename.nil?
|
41
|
+
write_lock_info(filename, position)
|
42
|
+
response = message.response
|
43
|
+
response.args = [ filename, position ]
|
44
|
+
response.status_note = 'Locked'
|
45
|
+
else
|
46
|
+
response = message.response
|
47
|
+
response.status_note = "No binlog information - can't lock."
|
48
|
+
end
|
49
|
+
|
50
|
+
response
|
51
|
+
end
|
52
|
+
|
53
|
+
def unlock(host, user, password)
|
54
|
+
locker = ::Emissary::Agent::Mysql::Helper.new(host, user, password)
|
55
|
+
raise "The database was not locked! (Possibly timed out.)" unless locker.locked?
|
56
|
+
|
57
|
+
locker.unlock!
|
58
|
+
|
59
|
+
response = message.response
|
60
|
+
response.status_note = 'Unlocked'
|
61
|
+
response
|
62
|
+
end
|
63
|
+
|
64
|
+
def status(host, user, password)
|
65
|
+
locker = ::Emissary::Agent::Mysql::Helper.new(host, user, password)
|
66
|
+
|
67
|
+
response = message.response
|
68
|
+
response.status_note = locker.locked? ? 'Locked' : 'Unlocked'
|
69
|
+
response
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def write_lock_info(filename, position)
|
75
|
+
File.open(coordinates_file, "w") do |file|
|
76
|
+
file << "#{filename},#{position}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
class Agent::Mysql::Helper
|
83
|
+
DEFAULT_TIMEOUT = 30
|
84
|
+
|
85
|
+
@@class_monitor = Monitor.new
|
86
|
+
|
87
|
+
# only return one locker per host+user combination
|
88
|
+
def self.new(host, user, password, timeout = nil)
|
89
|
+
@@class_monitor.synchronize do
|
90
|
+
(@@lockers||={})["#{host}:#{user}"] ||= begin
|
91
|
+
allocate.instance_eval(<<-EOS, __FILE__, __LINE__)
|
92
|
+
initialize(host, user, password, timeout || DEFAULT_TIMEOUT)
|
93
|
+
self
|
94
|
+
EOS
|
95
|
+
end
|
96
|
+
@@lockers["#{host}:#{user}"].timeout = timeout unless timeout.nil?
|
97
|
+
@@lockers["#{host}:#{user}"]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@@locked_M = Mutex.new
|
102
|
+
def locked_M() @@locked_M; end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def initialize(host, user, password, timeout = DEFAULT_TIMEOUT)
|
107
|
+
@host = host
|
108
|
+
@user = user
|
109
|
+
@password = password
|
110
|
+
@timeout = timeout
|
111
|
+
|
112
|
+
@watcher = nil
|
113
|
+
@connection = nil
|
114
|
+
@locked = false
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def connection
|
119
|
+
@connection ||= ::Mysql.real_connect(@host, @user, @password)
|
120
|
+
end
|
121
|
+
|
122
|
+
def disconnect
|
123
|
+
unless not connected?
|
124
|
+
puts "disconnecting.."
|
125
|
+
@connection.close
|
126
|
+
@connection = nil
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
public
|
131
|
+
attr_accessor :timeout
|
132
|
+
|
133
|
+
def connected?
|
134
|
+
!!@connection
|
135
|
+
end
|
136
|
+
|
137
|
+
# Acquire a lock and, with that lock, run a block/closure.
|
138
|
+
def with_lock
|
139
|
+
begin
|
140
|
+
lock! && yield
|
141
|
+
ensure
|
142
|
+
unlock!
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def locked?
|
147
|
+
!!@locked
|
148
|
+
end
|
149
|
+
|
150
|
+
def lock!
|
151
|
+
unless locked?
|
152
|
+
kill_watcher_thread! # make sure we have a new thread for watching
|
153
|
+
locked_M.synchronize { @locked = true }
|
154
|
+
connection.query("FLUSH TABLES WITH READ LOCK")
|
155
|
+
spawn_lockwatch_thread!
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def unlock!
|
160
|
+
begin
|
161
|
+
unless not locked?
|
162
|
+
locked_M.synchronize {
|
163
|
+
connection.query("UNLOCK TABLES")
|
164
|
+
@locked = false
|
165
|
+
}
|
166
|
+
end
|
167
|
+
ensure
|
168
|
+
kill_watcher_thread!
|
169
|
+
disconnect
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Test whether our login info is valid by attempting a database
|
174
|
+
# connection.
|
175
|
+
def valid?
|
176
|
+
begin
|
177
|
+
!!connection
|
178
|
+
rescue => e
|
179
|
+
false # Don't throw an exception, just return false.
|
180
|
+
ensure
|
181
|
+
disconnect if connected?
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# Returns [file, position]
|
186
|
+
def get_binlog_info
|
187
|
+
raise "get_binlog_info must be called from within a lock." unless locked?
|
188
|
+
(result = connection.query("SHOW MASTER STATUS")).fetch_row[0,2]
|
189
|
+
ensure
|
190
|
+
result.free
|
191
|
+
end
|
192
|
+
|
193
|
+
def spawn_lockwatch_thread!
|
194
|
+
if @watcher.is_a?(Thread) and not @watcher.alive?
|
195
|
+
puts "Watcher is dead - restarting"
|
196
|
+
@watcher = nil
|
197
|
+
end
|
198
|
+
|
199
|
+
@watcher ||= Thread.new {
|
200
|
+
begin
|
201
|
+
puts "Entering Watcher Loop"
|
202
|
+
Timeout.timeout(@timeout) do
|
203
|
+
loop { break unless locked? }
|
204
|
+
end
|
205
|
+
rescue Timeout::Error
|
206
|
+
ensure
|
207
|
+
unlock!
|
208
|
+
Thread.exit
|
209
|
+
end
|
210
|
+
}
|
211
|
+
end
|
212
|
+
|
213
|
+
def kill_watcher_thread!
|
214
|
+
@watcher.kill unless not @watcher.is_a?(Thread) or not @watcher.alive?
|
215
|
+
@watcher = nil
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Copyright 2010 The New York Times
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
#
|
15
|
+
#
|
16
|
+
module Emissary
|
17
|
+
class Agent::Ping < Agent
|
18
|
+
def valid_methods
|
19
|
+
[:ping, :pong]
|
20
|
+
end
|
21
|
+
|
22
|
+
def ping
|
23
|
+
reply = message.response
|
24
|
+
reply.method = :pong
|
25
|
+
|
26
|
+
::Emissary.logger.debug "Received PING: originator: #{message.originator}"
|
27
|
+
::Emissary.logger.debug "Sending PONG : originator: #{reply.originator}"
|
28
|
+
|
29
|
+
reply
|
30
|
+
end
|
31
|
+
|
32
|
+
def pong
|
33
|
+
::Emissary.logger.debug "Received PONG"
|
34
|
+
throw :skip_implicit_response
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Copyright 2010 The New York Times
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
#
|
15
|
+
#
|
16
|
+
module Emissary
|
17
|
+
class Agent::Proxy < Agent
|
18
|
+
def valid_methods
|
19
|
+
[ :any ]
|
20
|
+
end
|
21
|
+
|
22
|
+
def activate
|
23
|
+
throw :skip_implicit_response
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
# Copyright 2010 The New York Times
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
#
|
15
|
+
#
|
16
|
+
require 'escape'
|
17
|
+
|
18
|
+
module Emissary
|
19
|
+
class Agent::Rabbitmq < Agent
|
20
|
+
NIMBUL_VHOST = '/nimbul'
|
21
|
+
|
22
|
+
NODE_CONFIG_ACL = '^i-[a-f0-9.]+$'
|
23
|
+
NODE_READ_ACL = '^(amq.*|i-[a-f0-9.]+|request.%%ID%%.*)$'
|
24
|
+
NODE_WRITE_ACL = '^(amq.*|i-[a-f0-9.]+|(startup|info|shutdown).%%ID%%.*|nimbul)$'
|
25
|
+
|
26
|
+
QUEUE_INFO_ITEMS = %w[
|
27
|
+
name durable auto_delete arguments pid owner_pid
|
28
|
+
exclusive_consumer_pid exclusive_consumer_tag
|
29
|
+
messages_ready messages_unacknowledged messages_uncommitted
|
30
|
+
messages acks_uncommitted consumers transactions memory
|
31
|
+
]
|
32
|
+
|
33
|
+
EXCHANGE_INFO_ITEMS = %w[
|
34
|
+
name type durable auto_delete arguments
|
35
|
+
]
|
36
|
+
|
37
|
+
CONNECTION_INFO_ITEMS = %w[
|
38
|
+
pid address port peer_address peer_port state channels user
|
39
|
+
vhost timeout frame_max client_properties recv_oct recv_cnt
|
40
|
+
send_oct send_cnt send_pend
|
41
|
+
]
|
42
|
+
|
43
|
+
CHANNEL_INFO_ITEMS = %w[
|
44
|
+
pid connection number user vhost transactional consumer_count
|
45
|
+
messages_unacknowledged acks_uncommitted prefetch_count
|
46
|
+
]
|
47
|
+
|
48
|
+
BINDINGS_INFO_COLUMNS = %w[ exchange_name queue_name routing_key arguments ]
|
49
|
+
CONSUMER_INFO_COLUMNS = %w[ queue_name channel_process_id consumer_tag must_acknowledge ]
|
50
|
+
|
51
|
+
class CommandExecutionError < StandardError; end
|
52
|
+
|
53
|
+
def valid_methods
|
54
|
+
[
|
55
|
+
:add_user,
|
56
|
+
:delete_user,
|
57
|
+
:change_password,
|
58
|
+
:list_users,
|
59
|
+
|
60
|
+
:add_vhost,
|
61
|
+
:delete_vhost,
|
62
|
+
:list_vhosts,
|
63
|
+
|
64
|
+
:add_node_account,
|
65
|
+
:del_node_account,
|
66
|
+
|
67
|
+
:list_user_vhosts,
|
68
|
+
:list_vhost_users,
|
69
|
+
|
70
|
+
:list_queues,
|
71
|
+
:list_bindings,
|
72
|
+
:list_exchanges,
|
73
|
+
:list_connections,
|
74
|
+
:list_channels,
|
75
|
+
:list_consumers
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
def list_queues(vhost)
|
80
|
+
vhost = vhost.empty? ? '/' : vhost
|
81
|
+
rabbitmqctl(:list_queues, '-p', vhost, QUEUE_INFO_ITEMS.join(" ")).collect do |line|
|
82
|
+
Hash[*QUEUE_INFO_ITEMS.zip(line.split(/\s+/)).flatten]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def list_bindings(vhost)
|
87
|
+
vhost = vhost.empty? ? '/' : vhost
|
88
|
+
rabbitmqctl(:list_bindings, '-p', vhost).collect do |line|
|
89
|
+
Hash[*BINDINGS_INFO_COLUMNS.zip(line.split(/\s+/)).flatten]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def list_exchanges(vhost)
|
94
|
+
vhost = vhost.empty? ? '/' : vhost
|
95
|
+
rabbitmqctl(:list_exchanges, '-p', vhost, EXCHANGE_INFO_ITEMS.join(" ")).collect do |line|
|
96
|
+
Hash[*EXCHANGE_INFO_ITEMS.zip(line.split(/\s+/)).flatten]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def list_connections
|
101
|
+
rabbitmqctl(:list_connections, CONNECTION_INFO_ITEMS.join(" ")).collect do |line|
|
102
|
+
Hash[*CONNECTION_INFO_ITEMS.zip(line.split(/\s+/)).flatten]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def list_channels
|
107
|
+
rabbitmqctl(:list_channels, CHANNEL_INFO_ITEMS.join(" ")).collect do |line|
|
108
|
+
Hash[*CHANNEL_INFO_ITEMS.zip(line.split(/\s+/)).flatten]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def list_consumers(vhost)
|
113
|
+
vhost = vhost.empty? ? '/' : vhost
|
114
|
+
rabbitmqctl(:list_consumers, '-p', vhost).collect do |line|
|
115
|
+
Hash[*CONSUMER_INFO_COLUMNS.zip(line.split(/\s+/)).flatten]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def list_users
|
120
|
+
rabbitmqctl(:list_users)
|
121
|
+
end
|
122
|
+
|
123
|
+
def list_vhosts
|
124
|
+
rabbitmqctl(:list_vhosts)
|
125
|
+
end
|
126
|
+
|
127
|
+
def list_vhost_users(vhost)
|
128
|
+
vhost = vhost.empty? ? '/' : vhost
|
129
|
+
rabbitmqctl(:list_permissions, '-p', vhost).flatten.select { |l|
|
130
|
+
!l.nil?
|
131
|
+
}.collect {
|
132
|
+
|l| l.split(/\s+/)[0]
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def list_user_vhosts(user)
|
137
|
+
list_vhosts.select { |vhost| list_vhost_users(vhost).include? user }
|
138
|
+
end
|
139
|
+
|
140
|
+
def set_vhost_permissions(user, vhost, config, write, read)
|
141
|
+
vhost = vhost.empty? ? '/' : vhost
|
142
|
+
rabbitmqctl(:set_permissions, '-p', vhost, user, config, write, read)
|
143
|
+
end
|
144
|
+
|
145
|
+
def del_vhost_permissions(user, vhost)
|
146
|
+
vhost = vhost.empty? ? '/' : vhost
|
147
|
+
rabbitmqctl(:clear_permissions, '-p', vhost, user)
|
148
|
+
end
|
149
|
+
|
150
|
+
def add_node_account_acl(user, namespace_id)
|
151
|
+
config_acl = NODE_CONFIG_ACL.gsub('%%ID%%', namespace_id.to_s)
|
152
|
+
write_acl = NODE_WRITE_ACL.gsub('%%ID%%', namespace_id.to_s)
|
153
|
+
read_acl = NODE_READ_ACL.gsub('%%ID%%', namespace_id.to_s)
|
154
|
+
|
155
|
+
begin
|
156
|
+
set_vhost_permissions(user, NIMBUL_VHOST, config_acl, write_acl, read_acl)
|
157
|
+
rescue CommandExecutionError => e
|
158
|
+
"problem adding account acls for user: #{user}: #{e.message}"
|
159
|
+
else
|
160
|
+
"successfully added account acls for user: #{user}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def add_node_account(user, password, namespace_id)
|
165
|
+
begin
|
166
|
+
add_user(user, password)
|
167
|
+
add_node_account_acl(user, namespace_id.to_s)
|
168
|
+
rescue CommandExecutionError => e
|
169
|
+
"failed to add new node account: #{user}:#{namespace_id.to_s}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def del_node_account_acl(user, vhost)
|
174
|
+
begin
|
175
|
+
del_vhost_permissions(user, vhost)
|
176
|
+
rescue CommandExecutionError => e
|
177
|
+
"problem unmapping user from vhost: #{user}:#{vhost} #{e.message}"
|
178
|
+
else
|
179
|
+
"successfully unmapped user from vhost: #{user}:#{vhost}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def add_vhost(path)
|
184
|
+
begin
|
185
|
+
!!rabbitmqctl(:add_vhost, path)
|
186
|
+
rescue CommandExecutionError => e
|
187
|
+
raise e unless e.message.include? 'vhost_already_exists'
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def add_user(user, pass)
|
192
|
+
begin
|
193
|
+
!!rabbitmqctl(:add_user, user, pass)
|
194
|
+
rescue CommandExecutionError => e
|
195
|
+
raise e unless e.message.include? 'user_already_exists'
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def change_password(user, pass)
|
200
|
+
begin
|
201
|
+
!!rabbitmqctl(:change_password, user, pass)
|
202
|
+
rescue CommandExecutionError => e
|
203
|
+
return false if e.message.include? 'no_such_user'
|
204
|
+
raise e
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def delete_user(user)
|
209
|
+
begin
|
210
|
+
!!rabbitmqctl(:delete_user, user)
|
211
|
+
rescue CommandExecutionError => e
|
212
|
+
raise e unless e.message.include? 'no_such_user'
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def delete_vhost(path)
|
217
|
+
begin
|
218
|
+
!!rabbitmqctl(:delete_vhost, path)
|
219
|
+
rescue CommandExecutionError => e
|
220
|
+
raise e unless e.message.include? 'no_such_vhost'
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def rabbitmqctl(*args)
|
225
|
+
result = []
|
226
|
+
`rabbitmqctl #{Escape.shell_command([*args.collect{|a| a.to_s}])} 2>&1`.each do |line|
|
227
|
+
raise CommandExecutionError, $1 if line =~ /Error: (.*)/
|
228
|
+
result << line.chomp unless line =~ /\.\.\./
|
229
|
+
end
|
230
|
+
result
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|