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,152 @@
|
|
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 'digest/md5'
|
17
|
+
require 'base64'
|
18
|
+
require 'etc'
|
19
|
+
|
20
|
+
module Emissary
|
21
|
+
class Agent::Sshkeys < Agent
|
22
|
+
AUTH_KEY_FILE = '.ssh/authorized_keys'
|
23
|
+
|
24
|
+
attr_reader :user
|
25
|
+
|
26
|
+
def valid_methods
|
27
|
+
[ :add, :delete, :update ]
|
28
|
+
end
|
29
|
+
|
30
|
+
def post_init
|
31
|
+
begin
|
32
|
+
@user = Etc.getpwnam(args.shift)
|
33
|
+
rescue ArgumentError => e
|
34
|
+
if e.message =~ /can't find user/
|
35
|
+
raise "User '#{args.first}' does not exist on this system"
|
36
|
+
else
|
37
|
+
raise "Unhandled error attempting to retrieve data on user [#{args.first}]: #{e.message}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def add pubkey
|
43
|
+
raise "Missing 'key_uri' argument - can't download key!" if pubkey.nil?
|
44
|
+
|
45
|
+
pubkey_name = Digest::MD5.hexdigest(pubkey.split(/\s+/).join(' ').chomp)
|
46
|
+
|
47
|
+
begin
|
48
|
+
keys = get_keys(user)
|
49
|
+
if not keys.has_key?(pubkey_name)
|
50
|
+
keys[pubkey_name] = pubkey
|
51
|
+
write_keys(user, keys)
|
52
|
+
result = "Successfully added key [#{pubkey_name}] to user [#{user.name}]"
|
53
|
+
else
|
54
|
+
result = "Could not add key [#{pubkey_name}] to user [#{user.name}] - key already exists!"
|
55
|
+
end
|
56
|
+
rescue Exception => e
|
57
|
+
raise Exception, 'Possibly unable to add user key - error was: ' + e.message, caller
|
58
|
+
end
|
59
|
+
|
60
|
+
return result
|
61
|
+
end
|
62
|
+
|
63
|
+
def delete pubkey
|
64
|
+
return 'No authorized_keys file - nothing changed' if not File.exists?(File.join(user.dir, AUTH_KEY_FILE))
|
65
|
+
|
66
|
+
keyname = Digest::MD5.hexdigest(pubkey)
|
67
|
+
begin
|
68
|
+
keys = get_keys(user)
|
69
|
+
if keys.has_key?(keyname)
|
70
|
+
keys.delete(keyname)
|
71
|
+
write_keys(user, keys)
|
72
|
+
result = "Successfully removed key [#{keyname}] from user [#{user.name}]"
|
73
|
+
else
|
74
|
+
result = "Could not remove key [#{keyname}] from user [#{user.name}] - key not there!"
|
75
|
+
end
|
76
|
+
rescue Exception => e
|
77
|
+
raise "Possibly unable to remove key #{keyname} for user #{user.name} - error was: #{e.message}"
|
78
|
+
end
|
79
|
+
|
80
|
+
return result
|
81
|
+
end
|
82
|
+
|
83
|
+
def update oldkey, newkey
|
84
|
+
return 'Not implemented'
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def write_keys(user, keys)
|
90
|
+
auth_file_do(user, 'w') do |fp|
|
91
|
+
fp << keys.values.join("\n")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_keys(user)
|
96
|
+
::Emissary.logger.debug "retreiving ssh keys from file #{File.join(user.dir, AUTH_KEY_FILE)}"
|
97
|
+
|
98
|
+
keys = {}
|
99
|
+
auth_file_do(user, 'r') do |fp|
|
100
|
+
fp.readlines.each do |line|
|
101
|
+
keys[Digest::MD5.hexdigest(line.chomp)] = line.chomp
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
return keys
|
106
|
+
end
|
107
|
+
|
108
|
+
def auth_file_setup(user)
|
109
|
+
user_auth_file = File.join(user.dir, AUTH_KEY_FILE)
|
110
|
+
user_ssh_dir = File.dirname(user_auth_file)
|
111
|
+
|
112
|
+
begin
|
113
|
+
if not File.directory?(user_ssh_dir)
|
114
|
+
Dir.mkdir(user_ssh_dir)
|
115
|
+
File.open(user_ssh_dir) do |f|
|
116
|
+
f.chown(user.uid, user.gid)
|
117
|
+
f.chmod(0700)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
if not File.exists?(user_auth_file)
|
121
|
+
File.open(user_auth_file, 'a') do |f|
|
122
|
+
f.chown(user.uid, user.gid)
|
123
|
+
f.chmod(0600)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
rescue Exception => e
|
127
|
+
raise "Error creating #{user_auth_file} -- #{e.message}"
|
128
|
+
end
|
129
|
+
|
130
|
+
return user_auth_file
|
131
|
+
end
|
132
|
+
|
133
|
+
def auth_file_do(user, mode = 'r', &block)
|
134
|
+
begin
|
135
|
+
auth_file = auth_file_setup(user)
|
136
|
+
File.open(auth_file, mode) do |f|
|
137
|
+
f.flock File::LOCK_EX
|
138
|
+
yield(f)
|
139
|
+
f.flock File::LOCK_UN
|
140
|
+
end
|
141
|
+
rescue Exception => e
|
142
|
+
case mode
|
143
|
+
when 'r': mode = 'reading from'
|
144
|
+
when 'a': mode = 'appending to'
|
145
|
+
when 'w': mode = 'writing to'
|
146
|
+
else mode = "unknown operation #{mode} for File.open() on "
|
147
|
+
end
|
148
|
+
raise "Error #{mode} file '#{auth_file}' -- #{e.message}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,96 @@
|
|
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::Stats < Agent
|
18
|
+
STATISTIC_TYPES = [ :cpu, :network, :disk ]
|
19
|
+
|
20
|
+
begin
|
21
|
+
require 'sys/cpu'
|
22
|
+
rescue LoadError
|
23
|
+
STATISTIC_TYPES.delete(:cpu)
|
24
|
+
::Emissary.logger.warning "Ruby Gem 'sys-cpu' doesn't appear to be present - removing statistic gather for cpu."
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
require 'ifconfig'
|
29
|
+
rescue LoadError
|
30
|
+
STATISTIC_TYPES.delete(:network)
|
31
|
+
::Emissary.logger.warning "Ruby Gem 'ifconfig' doesn't appear to be present - removing statistic gather for network."
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid_methods
|
35
|
+
[ :gather ]
|
36
|
+
end
|
37
|
+
|
38
|
+
def gather
|
39
|
+
message.recipient = "#{config[:stats][:queue_base]}:#{message.exchange_type.to_s}"
|
40
|
+
message.args = STATISTIC_TYPES.inject([]) do |args, type|
|
41
|
+
unless (data = self.__send__(type)).nil?
|
42
|
+
args << { type => data }
|
43
|
+
end
|
44
|
+
args
|
45
|
+
end
|
46
|
+
|
47
|
+
throw :skip_implicit_response unless not message.args.empty?
|
48
|
+
return message
|
49
|
+
end
|
50
|
+
|
51
|
+
def disk
|
52
|
+
@cmd = "/usr/bin/env df -B K -P -T -x devfs -x tmpfs | /usr/bin/env tail -n +2"
|
53
|
+
|
54
|
+
data = IO.popen(@cmd){ |f| f.readlines }.collect { |l| l.split(/\s+/) }
|
55
|
+
data.inject([]) { |data,line|
|
56
|
+
device = Hash[[:device, :type, :size, :used, :avail, :percent, :mount].zip(line.collect!{|v| v =~ /^\d+/ ? v[/^(\d+)/].to_i : v })]
|
57
|
+
|
58
|
+
::Emissary.logger.notice("[statistics] Disk#%s: type:%s mount:%s size:%d used:%d in-use:%d%%",
|
59
|
+
device[:device], device[:type], device[:mount], device[:size], device[:used], device[:percent]
|
60
|
+
)
|
61
|
+
|
62
|
+
data << device
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def cpu
|
67
|
+
load_average = Sys::CPU.load_avg
|
68
|
+
::Emissary.logger.notice "[statistics] CPU: #{load_average.join ', '}"
|
69
|
+
load_average
|
70
|
+
end
|
71
|
+
|
72
|
+
def network
|
73
|
+
interfaces = (ifconfig = IfconfigWrapper.new.parse).interfaces.inject([]) do |interfaces, name|
|
74
|
+
interfaces << (interface = {
|
75
|
+
:name => name,
|
76
|
+
:tx => ifconfig[name].tx.symbolize,
|
77
|
+
:rx => ifconfig[name].rx.symbolize,
|
78
|
+
:up => ifconfig[name].status,
|
79
|
+
:ips => ifconfig[name].addresses('inet').collect { |ip| ip.to_s }
|
80
|
+
})
|
81
|
+
|
82
|
+
::Emissary.logger.notice("[statistics] Network#%s: state:%s tx:%d rx:%d inet:%s",
|
83
|
+
name,
|
84
|
+
(interface[:up] ? 'up' : 'down'),
|
85
|
+
interface[:tx][:bytes],
|
86
|
+
interface[:rx][:bytes],
|
87
|
+
interface[:ips].join(',')
|
88
|
+
) unless interface.try(:[], :tx).nil?
|
89
|
+
|
90
|
+
interfaces
|
91
|
+
end
|
92
|
+
|
93
|
+
return interfaces
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,40 @@
|
|
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::Test < Agent
|
18
|
+
def valid_methods
|
19
|
+
[:test_raise]
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_raise klass, *args
|
23
|
+
::Emissary.logger.debug "TEST AGENT: #test(#{klass}, #{args.inspect})"
|
24
|
+
|
25
|
+
exception = nil
|
26
|
+
begin
|
27
|
+
e_klass = ::Emissary.klass_const(klass)
|
28
|
+
unless not e_klass.try(:new).try(:is_a?, Exception)
|
29
|
+
raise e_klass, *args
|
30
|
+
else
|
31
|
+
raise Exception, "#{e_klass.name.to_s rescue e_klass.to_s} is not a valid exception name!"
|
32
|
+
end
|
33
|
+
rescue Exception => e
|
34
|
+
exception = e
|
35
|
+
end
|
36
|
+
|
37
|
+
message.error exception
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,231 @@
|
|
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 'inifile'
|
17
|
+
|
18
|
+
#
|
19
|
+
# This class represents the INI file and can be used to parse INI files.
|
20
|
+
# Derived from IniFile gem, found on http://rubyforge.org/projects/inifile/
|
21
|
+
#
|
22
|
+
module Emissary
|
23
|
+
class ConfigParseError < ::Emissary::Error
|
24
|
+
def initialize(message)
|
25
|
+
super(Exception, message)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class ConfigValidationError < ::Emissary::Error
|
30
|
+
def initialize(message)
|
31
|
+
super(Exception, message)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class ConfigFile < IniFile
|
36
|
+
|
37
|
+
attr_reader :ini
|
38
|
+
def initialize( filename, opts = {} )
|
39
|
+
@line_number = 0
|
40
|
+
@fn = filename
|
41
|
+
@comment = opts[:comment] || '#'
|
42
|
+
@param = opts[:parameter] || '='
|
43
|
+
@debug = !!opts[:debug]
|
44
|
+
@ini = Hash.new {|h,k| h[k] = Hash.new}
|
45
|
+
|
46
|
+
@rgxp_comment = /^\s*$|^\s*[#{@comment}]/
|
47
|
+
@rgxp_section = /^\s*\[([^\]]+)\]/
|
48
|
+
@rgxp_param = /^([^#{@param}]+)#{@param}(.*)$/
|
49
|
+
|
50
|
+
@rgxp_dict_start = /^([^#{@param}]+)#{@param}\s*\{\s*$/
|
51
|
+
@rgxp_dict_stop = /^\s*\}\s*$/
|
52
|
+
@dict_stack = []
|
53
|
+
|
54
|
+
@rgxp_list_start = /^([^#{@param}]+)#{@param}\s*\[\s*$/
|
55
|
+
@rgxp_list_line = /^([^#{@param}]+)#{@param}\s*\[\s*([^\]]+)\]\s*$/
|
56
|
+
@rgxp_list_stop = /^\s*\]\s*$/
|
57
|
+
@list_items = []
|
58
|
+
@in_list_name = nil
|
59
|
+
|
60
|
+
super filename, opts
|
61
|
+
|
62
|
+
yield self if block_given?
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# call-seq:
|
67
|
+
# ini_file[section]
|
68
|
+
#
|
69
|
+
# Get the hash of parameter/value pairs for the given _section_.
|
70
|
+
#
|
71
|
+
def []( section )
|
72
|
+
return nil if section.nil?
|
73
|
+
@ini[section.to_sym]
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# call-seq:
|
78
|
+
# has_section?( section )
|
79
|
+
#
|
80
|
+
# Returns +true+ if the named _section_ exists in the INI file.
|
81
|
+
#
|
82
|
+
def has_section?( section )
|
83
|
+
@ini.has_key? section.to_sym
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# call-seq:
|
88
|
+
# parse
|
89
|
+
#
|
90
|
+
# Loops over each line of the file, passing it off to the parse_line method
|
91
|
+
#
|
92
|
+
def parse
|
93
|
+
return unless ::Kernel.test ?f, @fn
|
94
|
+
@section_name = nil
|
95
|
+
::File.open(@fn, 'r') do |f|
|
96
|
+
while line = f.gets
|
97
|
+
@line_number += 1
|
98
|
+
parse_line line.chomp
|
99
|
+
end
|
100
|
+
end
|
101
|
+
@section_name = nil
|
102
|
+
@line_number = 0
|
103
|
+
return
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# call-seq:
|
108
|
+
# set_vall( key, value) => value
|
109
|
+
#
|
110
|
+
# Sets the value of the given key taking the current stack level into account
|
111
|
+
#
|
112
|
+
def set_value key, value
|
113
|
+
begin
|
114
|
+
p = @ini[@section_name]
|
115
|
+
@dict_stack.map { |d| p = (p[d]||={}) }
|
116
|
+
p[key] = value
|
117
|
+
rescue NoMethodError
|
118
|
+
raise ConfigParseError, "sectionless parameter declaration encountered at line #{@line_number}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
#
|
125
|
+
# call-seq:
|
126
|
+
# current_state (param = nil) => state
|
127
|
+
#
|
128
|
+
# Used for outputing the current parameter hash heirarchy in debug mode
|
129
|
+
#
|
130
|
+
def current_state param = nil
|
131
|
+
state = "@ini[:#{@section_name}]"
|
132
|
+
state << @dict_stack.collect { |c| "[:#{c}]" }.join unless @dict_stack.empty?
|
133
|
+
state << "[:#{@in_list_name}]" unless @in_list_name.nil?
|
134
|
+
state << "[:#{param}]" unless param.nil?
|
135
|
+
state
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# call-seq:
|
140
|
+
# parse_line(line)
|
141
|
+
#
|
142
|
+
# Parses the given line
|
143
|
+
#
|
144
|
+
def parse_line line
|
145
|
+
line.gsub!(/\s+#.*$/, '') # strip comments
|
146
|
+
|
147
|
+
# replace __FILE__ with the file being parsed
|
148
|
+
line.gsub!('__FILE__', File.expand_path(@fn))
|
149
|
+
|
150
|
+
# replace __DIR__ with the path of the file being parsed
|
151
|
+
line.gsub!('__DIR__', File.dirname(File.expand_path(@fn)))
|
152
|
+
|
153
|
+
# replace __ID_<METHOD>__ with Emissary.identity.<method>
|
154
|
+
[ :name, :instance_id, :server_id, :cluster_id, :account_id ].each do |id_method|
|
155
|
+
line.gsub!("__ID_#{id_method.to_s.upcase}__", Emissary.identity.__send__(id_method).to_s)
|
156
|
+
end
|
157
|
+
|
158
|
+
if not @in_list_name.nil? and line !~ @rgxp_list_stop
|
159
|
+
line = line.strip.split(/\s*,\s*/).compact.reject(&:blank?)
|
160
|
+
Emissary.logger.debug " ---> LIST ITEM #{current_state} << #{line.inspect}" if @debug
|
161
|
+
# then we're in the middle of a list item, so add to it
|
162
|
+
@list_items = @list_items | line
|
163
|
+
return
|
164
|
+
end
|
165
|
+
|
166
|
+
case line
|
167
|
+
# ignore blank lines and comment lines
|
168
|
+
when @rgxp_comment: return
|
169
|
+
|
170
|
+
# this is a section declaration
|
171
|
+
when @rgxp_section
|
172
|
+
Emissary.logger.debug "SECTION: #{line}" if @debug
|
173
|
+
|
174
|
+
unless @in_dict_name.nil?
|
175
|
+
raise ConfigParseError, "dictionary '#{@in_dict_name}' crosses section '#{$1.strip.downcase}' boundary at line #{@line_number}"
|
176
|
+
end
|
177
|
+
|
178
|
+
@section_name = $1.strip.downcase.to_sym
|
179
|
+
@ini[@section_name] ||= {}
|
180
|
+
|
181
|
+
when @rgxp_dict_start
|
182
|
+
@dict_stack << $1.strip.downcase.to_sym
|
183
|
+
Emissary.logger.debug " ---> DICT_BEG: #{@dict_stack.last}" if @debug
|
184
|
+
|
185
|
+
when @rgxp_dict_stop
|
186
|
+
raise ConfigParseError, "end of dictionary found without beginning at line #{@line_number}" if @dict_stack.empty?
|
187
|
+
Emissary.logger.debug " ---> DICT_END: #{@dict_stack.last}" if @debug
|
188
|
+
@dict_stack.pop
|
189
|
+
return
|
190
|
+
|
191
|
+
when @rgxp_list_line
|
192
|
+
list_name = $1.strip.downcase.to_sym
|
193
|
+
list_items = $2.strip.split(/\s*,\s*/).compact.reject(&:blank?)
|
194
|
+
|
195
|
+
unless not @debug
|
196
|
+
Emissary.logger.debug " ---> LIST_BEG: #{list_name}"
|
197
|
+
list_items.each do |li|
|
198
|
+
Emissary.logger.debug " ---> LIST_ITEM: #{current_state list_name} << [\"#{li}\"]"
|
199
|
+
end
|
200
|
+
Emissary.logger.debug " ---> LIST_END: #{list_name}"
|
201
|
+
end
|
202
|
+
|
203
|
+
set_value list_name, list_items
|
204
|
+
|
205
|
+
when @rgxp_list_start
|
206
|
+
Emissary.logger.debug " ---> LIST_BEG: #{line}" if @debug
|
207
|
+
@in_list_name = $1.strip.downcase.to_sym
|
208
|
+
|
209
|
+
when @rgxp_list_stop
|
210
|
+
Emissary.logger.debug " ---> LIST_END: #{@in_list_name} - #{@list_items.inspect}" if @debug
|
211
|
+
raise ConfigParseError, "end of list found without beginning at line #{@line_number}" if @in_list_name.nil?
|
212
|
+
set_value @in_list_name, @list_items
|
213
|
+
|
214
|
+
@in_list_name = nil
|
215
|
+
@list_items = []
|
216
|
+
|
217
|
+
when @rgxp_param
|
218
|
+
val = $2.strip
|
219
|
+
val = val[1..-2] if val[0..0] == "'" || val[-1..-1] == '"'
|
220
|
+
|
221
|
+
key = $1.strip.downcase.to_sym
|
222
|
+
Emissary.logger.debug " ---> PARAM: #{current_state key} = #{val}" if @debug
|
223
|
+
set_value key, val
|
224
|
+
|
225
|
+
else
|
226
|
+
raise Exception, "Unable to parse line #{@line_number}: #{line}"
|
227
|
+
end
|
228
|
+
return true
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|