continuent-tools-core 0.0.1
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.
- checksums.yaml +7 -0
- data/LICENSE +202 -0
- data/README.md +44 -0
- data/lib/continuent-tools-core.rb +20 -0
- data/lib/tungsten.rb +46 -0
- data/lib/tungsten/README +297 -0
- data/lib/tungsten/api.rb +553 -0
- data/lib/tungsten/common.rb +190 -0
- data/lib/tungsten/exec.rb +570 -0
- data/lib/tungsten/install.rb +332 -0
- data/lib/tungsten/properties.rb +476 -0
- data/lib/tungsten/script.rb +952 -0
- data/lib/tungsten/status.rb +177 -0
- data/lib/tungsten/util.rb +523 -0
- metadata +154 -0
@@ -0,0 +1,332 @@
|
|
1
|
+
class TungstenInstall
|
2
|
+
def initialize(base_path)
|
3
|
+
unless self.class.is_installed?(base_path)
|
4
|
+
raise "Unable to use #{base_path} because it is not an installed Tungsten directory"
|
5
|
+
end
|
6
|
+
|
7
|
+
@root = TU.cmd_result("cat #{base_path}/.lock")
|
8
|
+
TU.debug("Initialize #{self.class.name} from #{@root}")
|
9
|
+
@settings = {}
|
10
|
+
@topology = nil
|
11
|
+
|
12
|
+
begin
|
13
|
+
@has_tpm = (TU.cmd_result("#{tpm()} query staging") != "")
|
14
|
+
if @has_tpm == false
|
15
|
+
@has_tpm = (TU.cmd_result("#{tpm()} query dataservices") != "")
|
16
|
+
end
|
17
|
+
rescue
|
18
|
+
@has_tpm = false
|
19
|
+
end
|
20
|
+
|
21
|
+
# Preload settings about this installation
|
22
|
+
if use_tpm?()
|
23
|
+
# Pull the values from tpm
|
24
|
+
settings([
|
25
|
+
"user",
|
26
|
+
"host_name",
|
27
|
+
HOST_ENABLE_REPLICATOR,
|
28
|
+
HOST_ENABLE_MANAGER,
|
29
|
+
HOST_ENABLE_CONNECTOR,
|
30
|
+
REPL_RMI_PORT,
|
31
|
+
MGR_RMI_PORT,
|
32
|
+
MGR_API,
|
33
|
+
MGR_API_PORT,
|
34
|
+
MGR_API_ADDRESS,
|
35
|
+
"preferred_path"
|
36
|
+
])
|
37
|
+
else
|
38
|
+
# Read the values from files
|
39
|
+
setting(HOST_ENABLE_REPLICATOR, "true")
|
40
|
+
setting(HOST_ENABLE_MANAGER, "false")
|
41
|
+
setting(HOST_ENABLE_CONNECTOR, "false")
|
42
|
+
setting(REPL_RMI_PORT, TU.cmd_result("grep rmi_port #{@root}/#{CURRENT_RELEASE_DIRECTORY}/tungsten-replicator/conf/services.properties | grep -v '^#' | awk -F= '{print $2}' | tr -d ' '"))
|
43
|
+
setting("host_name", TU.cmd_result("egrep '^replicator.host=' #{@root}/#{CURRENT_RELEASE_DIRECTORY}/tungsten-replicator/conf/services.properties | awk -F= '{print $2}'"))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.is_installed?(base_path)
|
48
|
+
if File.exists?("#{base_path}/.manifest") && File.exists?("#{base_path}/.lock")
|
49
|
+
return true
|
50
|
+
else
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def root
|
56
|
+
@root
|
57
|
+
end
|
58
|
+
|
59
|
+
def hostname
|
60
|
+
setting("host_name")
|
61
|
+
end
|
62
|
+
|
63
|
+
def user
|
64
|
+
# Access the array directly to avoid an infinite loop
|
65
|
+
@settings["user"]
|
66
|
+
end
|
67
|
+
|
68
|
+
def dataservices
|
69
|
+
ds_list = TU.cmd_result("egrep \"^service.name\" #{@root}/#{CURRENT_RELEASE_DIRECTORY}/tungsten-replicator/conf/static-* | awk -F \"=\" '{print $2}'").split("\n")
|
70
|
+
|
71
|
+
if use_tpm?()
|
72
|
+
ds_list = ds_list + TU.cmd_result("#{tpm()} query dataservices | grep COMPOSITE | awk -F \" \" '{print $1}'").split("\n")
|
73
|
+
end
|
74
|
+
|
75
|
+
ds_list.uniq()
|
76
|
+
end
|
77
|
+
|
78
|
+
def default_dataservice
|
79
|
+
if is_manager?()
|
80
|
+
setting("dataservice_name")
|
81
|
+
elsif is_replicator?()
|
82
|
+
local_services = TU.cmd_result("egrep -l \"^replicator.service.type=local\" #{@root}/#{CURRENT_RELEASE_DIRECTORY}/tungsten-replicator/conf/static*").split("\n")
|
83
|
+
if local_services.size() == 0
|
84
|
+
dataservices().get(0)
|
85
|
+
else
|
86
|
+
TU.cmd_result("egrep \"^service.name\" #{local_services[0]} | awk -F \"=\" '{print $2}'")
|
87
|
+
end
|
88
|
+
else
|
89
|
+
dataservices()[0]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def replication_services
|
94
|
+
TU.cmd_result("egrep \"^service.name\" #{@root}/#{CURRENT_RELEASE_DIRECTORY}/tungsten-replicator/conf/static-* | awk -F \"=\" '{print $2}'").split("\n")
|
95
|
+
end
|
96
|
+
|
97
|
+
def tpm
|
98
|
+
"#{tungsten_sudo_prefix()}#{@root}/#{CURRENT_RELEASE_DIRECTORY}/tools/tpm"
|
99
|
+
end
|
100
|
+
|
101
|
+
def setting(key, v = nil)
|
102
|
+
if v == nil
|
103
|
+
return settings([key])[key]
|
104
|
+
else
|
105
|
+
@settings[key] = v
|
106
|
+
|
107
|
+
return v
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def settings(keys)
|
112
|
+
remaining_keys = keys - @settings.keys()
|
113
|
+
if remaining_keys.size() > 0
|
114
|
+
if use_tpm?()
|
115
|
+
begin
|
116
|
+
JSON.parse(TU.cmd_result("#{tpm()} query values #{remaining_keys.join(' ')}")).each{
|
117
|
+
|k, v|
|
118
|
+
@settings[k] = v
|
119
|
+
}
|
120
|
+
rescue => e
|
121
|
+
TU.exception(e)
|
122
|
+
raise "Unable to load tpm values #{keys.join(' ')}"
|
123
|
+
end
|
124
|
+
else
|
125
|
+
TU.debug("Unable to autodetect settings because tpm was not used to install this directory")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
return_values = {}
|
130
|
+
keys.each{
|
131
|
+
|k|
|
132
|
+
return_values[k] = @settings[k]
|
133
|
+
}
|
134
|
+
return_values
|
135
|
+
end
|
136
|
+
|
137
|
+
def setting_key(first, second, third = nil)
|
138
|
+
if first == CONNECTORS
|
139
|
+
"#{first}.#{TU.to_identifier(hostname())}.#{second}"
|
140
|
+
elsif first == DATASERVICES
|
141
|
+
if third == nil
|
142
|
+
raise "Unable to create setting key for #{first}.#{second}"
|
143
|
+
end
|
144
|
+
"#{first}.#{TU.to_identifier(second)}.#{third}"
|
145
|
+
elsif first == HOSTS
|
146
|
+
"#{first}.#{TU.to_identifier(hostname())}.#{second}"
|
147
|
+
elsif first == MANAGERS
|
148
|
+
if third == nil
|
149
|
+
raise "Unable to create setting key for #{first}.#{second}"
|
150
|
+
end
|
151
|
+
"#{first}.#{TU.to_identifier(second)}_#{TU.to_identifier(hostname())}.#{third}"
|
152
|
+
elsif first == REPL_SERVICES
|
153
|
+
if third == nil
|
154
|
+
raise "Unable to create setting key for #{first}.#{second}"
|
155
|
+
end
|
156
|
+
"#{first}.#{TU.to_identifier(second)}_#{TU.to_identifier(hostname())}.#{third}"
|
157
|
+
else
|
158
|
+
"#{first}.#{TU.to_identifier(hostname())}.#{second}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def cctrl
|
163
|
+
"#{tungsten_sudo_prefix()}#{@root}/#{CURRENT_RELEASE_DIRECTORY}/tungsten-manager/bin/cctrl -expert -port #{setting(MGR_RMI_PORT)}"
|
164
|
+
end
|
165
|
+
|
166
|
+
def mgr_api_uri
|
167
|
+
if setting(MGR_API_ADDRESS) == "0.0.0.0"
|
168
|
+
"#{hostname()}:#{setting(MGR_API_PORT)}"
|
169
|
+
else
|
170
|
+
"#{setting(MGR_API_ADDRESS)}:#{setting(MGR_API_PORT)}"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def status(dataservice = nil)
|
175
|
+
if dataservice == nil
|
176
|
+
dataservice = default_dataservice()
|
177
|
+
end
|
178
|
+
|
179
|
+
unless dataservices().include?(dataservice)
|
180
|
+
raise "Unable to provide a status for #{dataservice} because it is not defined on this host"
|
181
|
+
end
|
182
|
+
|
183
|
+
return TungstenStatus.new(self, dataservice)
|
184
|
+
end
|
185
|
+
|
186
|
+
def topology(dataservice = nil)
|
187
|
+
if dataservice == nil
|
188
|
+
dataservice = default_dataservice()
|
189
|
+
end
|
190
|
+
|
191
|
+
unless dataservices().include?(dataservice)
|
192
|
+
raise "Unable to provide a topology for #{dataservice} because it is not defined on this host"
|
193
|
+
end
|
194
|
+
|
195
|
+
return TungstenTopology.new(self, dataservice)
|
196
|
+
end
|
197
|
+
|
198
|
+
def trepctl(service)
|
199
|
+
"#{tungsten_sudo_prefix()}#{@root}/#{CURRENT_RELEASE_DIRECTORY}/tungsten-replicator/bin/trepctl -port #{setting(REPL_RMI_PORT)} -service #{service}"
|
200
|
+
end
|
201
|
+
|
202
|
+
def trepctl_value(service, key)
|
203
|
+
TU.cmd_result("#{trepctl(service)} status | grep #{key} | awk -F: '{print $2}' | tr -d ' '")
|
204
|
+
end
|
205
|
+
|
206
|
+
def trepctl_property(service, key)
|
207
|
+
properties = JSON.parse(TU.cmd_result("#{trepctl(service)} properties -filter #{key}"))
|
208
|
+
if properties.has_key?(key)
|
209
|
+
return properties[key]
|
210
|
+
else
|
211
|
+
raise "Unable to find a value for #{key} in the output of `trepctl -service #{service} properties`."
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def thl(service)
|
216
|
+
"#{tungsten_sudo_prefix()}#{@root}/#{CURRENT_RELEASE_DIRECTORY}/tungsten-replicator/bin/thl -service #{service}"
|
217
|
+
end
|
218
|
+
|
219
|
+
def service_path(component)
|
220
|
+
"#{@root}/#{CURRENT_RELEASE_DIRECTORY}/tungsten-#{component}/bin/#{component}"
|
221
|
+
end
|
222
|
+
|
223
|
+
def is_running?(component)
|
224
|
+
begin
|
225
|
+
TU.cmd_result("#{service_path(component)} status")
|
226
|
+
return true
|
227
|
+
rescue CommandError
|
228
|
+
return false
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def is_replicator?
|
233
|
+
(setting(HOST_ENABLE_REPLICATOR) == "true")
|
234
|
+
end
|
235
|
+
|
236
|
+
def is_manager?
|
237
|
+
(setting(HOST_ENABLE_MANAGER) == "true")
|
238
|
+
end
|
239
|
+
|
240
|
+
def is_connector?
|
241
|
+
(setting(HOST_ENABLE_CONNECTOR) == "true")
|
242
|
+
end
|
243
|
+
|
244
|
+
def is_commercial?
|
245
|
+
File.exists?("#{@root}/#{CURRENT_RELEASE_DIRECTORY}/tungsten-manager")
|
246
|
+
end
|
247
|
+
|
248
|
+
def use_tpm?
|
249
|
+
@has_tpm
|
250
|
+
end
|
251
|
+
|
252
|
+
def ensure_cctrl(cmd, max_tries = 5)
|
253
|
+
i=0
|
254
|
+
while (i<max_tries)
|
255
|
+
begin
|
256
|
+
return TU.cmd_result("echo #{cmd}| #{cctrl}")
|
257
|
+
rescue CommandError
|
258
|
+
TU.debug(e)
|
259
|
+
end
|
260
|
+
i+=1
|
261
|
+
end
|
262
|
+
|
263
|
+
raise "Unable to execute '#{cmd}' in cctrl"
|
264
|
+
end
|
265
|
+
|
266
|
+
def inherit_path
|
267
|
+
if setting("preferred_path") != ""
|
268
|
+
ENV['PATH'] = setting("preferred_path").to_s() + ":" + ENV['PATH']
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# Build a sudo prefix to run a command as the tungsten system user
|
273
|
+
def tungsten_sudo_prefix
|
274
|
+
if self.user() == nil || ENV['USER'] == self.user()
|
275
|
+
return ""
|
276
|
+
else
|
277
|
+
return "sudo -u #{self.user()} -n -i "
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def self.get(path)
|
282
|
+
@@instances ||= {}
|
283
|
+
unless @@instances.has_key?(path)
|
284
|
+
@@instances[path] = TungstenInstall.new(path)
|
285
|
+
end
|
286
|
+
return @@instances[path]
|
287
|
+
end
|
288
|
+
|
289
|
+
class TungstenTopology
|
290
|
+
attr_reader :datasources, :master, :connectors, :dataservices, :type
|
291
|
+
|
292
|
+
def initialize(install, dataservice)
|
293
|
+
@install = install
|
294
|
+
@name = dataservice
|
295
|
+
|
296
|
+
unless @install.use_tpm?()
|
297
|
+
raise "Unable to parse the topology for #{@name} from #{@install.hostname()}:#{@install.root()} because tpm was not used for installation"
|
298
|
+
end
|
299
|
+
|
300
|
+
values = @install.settings([
|
301
|
+
"dataservices.#{@name}.dataservice_hosts",
|
302
|
+
"dataservices.#{@name}.dataservice_master_host",
|
303
|
+
"dataservices.#{@name}.dataservice_connectors",
|
304
|
+
"dataservices.#{@name}.dataservice_composite_datasources",
|
305
|
+
"dataservices.#{@name}.dataservice_topology"
|
306
|
+
])
|
307
|
+
|
308
|
+
@type = values["dataservices.#{@name}.dataservice_topology"]
|
309
|
+
@members = values["dataservices.#{@name}.dataservice_hosts"].to_s().split(",")
|
310
|
+
@master = values["dataservices.#{@name}.dataservice_master_host"]
|
311
|
+
@connectors = values["dataservices.#{@name}.dataservice_connectors"].to_s().split(",")
|
312
|
+
@dataservices = values["dataservices.#{@name}.dataservice_composite_datasources"].to_s().split(",")
|
313
|
+
end
|
314
|
+
|
315
|
+
def is_composite?
|
316
|
+
(@dataservices.size() > 0)
|
317
|
+
end
|
318
|
+
|
319
|
+
def to_hash
|
320
|
+
{
|
321
|
+
:hostname => @install.hostname(),
|
322
|
+
:root => @install.root(),
|
323
|
+
:is_composite => is_composite?(),
|
324
|
+
:type => @type,
|
325
|
+
:members => @members,
|
326
|
+
:master => @master,
|
327
|
+
:connectors => @connectors,
|
328
|
+
:dataservices => @dataservices
|
329
|
+
}
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
@@ -0,0 +1,476 @@
|
|
1
|
+
#
|
2
|
+
# TUNGSTEN SCALE-OUT STACK
|
3
|
+
# Copyright (C) 2009 Continuent, Inc.
|
4
|
+
# All rights reserved
|
5
|
+
#
|
6
|
+
|
7
|
+
require "date"
|
8
|
+
|
9
|
+
# Defines a properties object.
|
10
|
+
class Properties
|
11
|
+
attr_accessor :props,:use_prompt_handler,:force_json
|
12
|
+
|
13
|
+
# Initialize with some base values.
|
14
|
+
def initialize
|
15
|
+
@props = {}
|
16
|
+
@in_prompt_handler = {}
|
17
|
+
@in_template_value_prompt_handler = {}
|
18
|
+
@use_prompt_handler = false
|
19
|
+
@prompt_handler = nil
|
20
|
+
@force_json = true
|
21
|
+
@debug = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize_copy(source)
|
25
|
+
super(source)
|
26
|
+
@props = Marshal::load(Marshal::dump(@props))
|
27
|
+
@in_prompt_handler = {}
|
28
|
+
@in_template_value_prompt_handler = {}
|
29
|
+
@use_prompt_handler = source.use_prompt_handler
|
30
|
+
@prompt_handler = nil
|
31
|
+
@force_json = source.force_json
|
32
|
+
end
|
33
|
+
|
34
|
+
# Read properties from a file.
|
35
|
+
def load(properties_filename)
|
36
|
+
file_contents = ""
|
37
|
+
|
38
|
+
File.open(properties_filename, 'r') do |file|
|
39
|
+
file.read.each_line do |line|
|
40
|
+
line.strip!
|
41
|
+
unless (line =~ /^#.*/)
|
42
|
+
file_contents = file_contents + line
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
begin
|
47
|
+
parsed_contents = JSON.parse(file_contents)
|
48
|
+
rescue Exception => e
|
49
|
+
if file_contents == ""
|
50
|
+
parsed_contents = {}
|
51
|
+
elsif file_contents[0,1] == "{"
|
52
|
+
raise "There was an error parsing the config file: #{e.message}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
if parsed_contents && parsed_contents.instance_of?(Hash)
|
57
|
+
@props = parsed_contents
|
58
|
+
elsif @force_json == true
|
59
|
+
raise "There was an error parsing the JSON config file: #{properties_filename}. Try using the migration procedure if you have an old configuration file."
|
60
|
+
else
|
61
|
+
new_props = {}
|
62
|
+
|
63
|
+
file.rewind()
|
64
|
+
file.read.each_line do |line|
|
65
|
+
line.strip!
|
66
|
+
|
67
|
+
if (line =~ /^([\w\.]+)\[?([\w\.]+)?\]?\s*=\s*(\S.*)/)
|
68
|
+
key = $1
|
69
|
+
value = $3
|
70
|
+
|
71
|
+
if $2
|
72
|
+
new_props[key] = {} unless new_props[key]
|
73
|
+
new_props[key][$2] = value
|
74
|
+
else
|
75
|
+
new_props[key] = value
|
76
|
+
end
|
77
|
+
elsif (line =~ /^([\w\.]+)\s*=/)
|
78
|
+
key = $1
|
79
|
+
value = ""
|
80
|
+
new_props[key] = value
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
@props = new_props
|
85
|
+
end
|
86
|
+
|
87
|
+
original_props = @props.dup
|
88
|
+
|
89
|
+
if original_props != @props
|
90
|
+
Configurator.instance.warning("Deprecated keys in the config file were updated")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Read properties from a file.
|
96
|
+
def load_and_initialize(properties_filename, keys_module)
|
97
|
+
load(properties_filename)
|
98
|
+
init(keys_module)
|
99
|
+
end
|
100
|
+
|
101
|
+
def reset
|
102
|
+
self.props = {}
|
103
|
+
end
|
104
|
+
|
105
|
+
def debug(v = nil)
|
106
|
+
if v != nil
|
107
|
+
@debug = v
|
108
|
+
end
|
109
|
+
|
110
|
+
v
|
111
|
+
end
|
112
|
+
|
113
|
+
def import(properties_obj = {})
|
114
|
+
if properties_obj.instance_of?(Hash)
|
115
|
+
self.props = properties_obj
|
116
|
+
elsif properties_obj.instance_of?(Properties)
|
117
|
+
self.props = properties_obj.props
|
118
|
+
else
|
119
|
+
raise "You must pass in a Hash or Properties object to import"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Write properties to a file. We use signal protection to avoid getting
|
124
|
+
# interrupted half-way through.
|
125
|
+
def store(properties_filename, use_json = true)
|
126
|
+
# Protect I/O with trap for Ctrl-C.
|
127
|
+
interrupted = false
|
128
|
+
old_trap = trap("INT") {
|
129
|
+
interrupted = true;
|
130
|
+
}
|
131
|
+
|
132
|
+
# Write.
|
133
|
+
File.open(properties_filename, 'w') do |file|
|
134
|
+
file.printf "# Tungsten configuration properties\n"
|
135
|
+
file.printf "# Date: %s\n", DateTime.now
|
136
|
+
|
137
|
+
if use_json == false
|
138
|
+
@props.sort.each do | key, value |
|
139
|
+
file.printf "%s=%s\n", key, value
|
140
|
+
end
|
141
|
+
else
|
142
|
+
file.print self.to_s
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Check for interrupt and restore handler.
|
147
|
+
if (interrupted)
|
148
|
+
puts
|
149
|
+
puts ("Configuration interrupted")
|
150
|
+
exit 1;
|
151
|
+
else
|
152
|
+
trap("INT", old_trap);
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Return the size of the properties object.
|
157
|
+
def size()
|
158
|
+
@props.size
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_s
|
162
|
+
JSON.pretty_generate(@props)
|
163
|
+
end
|
164
|
+
|
165
|
+
def output()
|
166
|
+
TU.output(self.to_s)
|
167
|
+
end
|
168
|
+
|
169
|
+
def force_output()
|
170
|
+
TU.force_output(self.to_s)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Fetch a nested hash value
|
174
|
+
def getNestedProperty(attrs)
|
175
|
+
if attrs.is_a?(String)
|
176
|
+
attrs = attrs.split('.')
|
177
|
+
end
|
178
|
+
|
179
|
+
attr_count = attrs.size
|
180
|
+
current_val = @props
|
181
|
+
for i in 0..(attr_count-1)
|
182
|
+
attr_name = attrs[i]
|
183
|
+
return current_val[attr_name] if i == (attr_count-1)
|
184
|
+
return nil if current_val[attr_name].nil?
|
185
|
+
current_val = current_val[attr_name]
|
186
|
+
end
|
187
|
+
|
188
|
+
return nil
|
189
|
+
end
|
190
|
+
|
191
|
+
def setNestedProperty(new_val, attrs)
|
192
|
+
attr_count = attrs.size
|
193
|
+
current_val = @props
|
194
|
+
for i in 0..(attr_count-1)
|
195
|
+
attr_name = attrs[i]
|
196
|
+
if i == (attr_count-1)
|
197
|
+
return setHashProperty(current_val, attr_name, new_val)
|
198
|
+
end
|
199
|
+
current_val[attr_name] = {} if current_val[attr_name].nil?
|
200
|
+
current_val = current_val[attr_name]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def setHashProperty(hash, key, value)
|
205
|
+
if value == nil || value == []
|
206
|
+
return (hash.delete(key))
|
207
|
+
else
|
208
|
+
if value.is_a?(Hash)
|
209
|
+
hash[key] ||= {}
|
210
|
+
value.each{|sub_key,sub_value|
|
211
|
+
setHashProperty(hash[key], sub_key, sub_value)
|
212
|
+
}
|
213
|
+
else
|
214
|
+
return (hash[key] = value)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Get a property value.
|
220
|
+
def getProperty(key, allow_disabled = false)
|
221
|
+
if key.is_a?(String)
|
222
|
+
key_string = key
|
223
|
+
key = key.split('.')
|
224
|
+
else
|
225
|
+
key_string = key.join('.')
|
226
|
+
end
|
227
|
+
|
228
|
+
value = getNestedProperty(key)
|
229
|
+
if value != nil
|
230
|
+
return value
|
231
|
+
end
|
232
|
+
|
233
|
+
if usePromptHandler()
|
234
|
+
findProperty = lambda do |keys|
|
235
|
+
if @in_prompt_handler[key_string] == true
|
236
|
+
return nil
|
237
|
+
end
|
238
|
+
|
239
|
+
begin
|
240
|
+
@in_prompt_handler[key_string] = true
|
241
|
+
|
242
|
+
value = getPromptHandler().get_property(keys, allow_disabled)
|
243
|
+
|
244
|
+
@in_prompt_handler[key_string] = false
|
245
|
+
rescue IgnoreError
|
246
|
+
@in_prompt_handler[key_string] = false
|
247
|
+
rescue => e
|
248
|
+
@in_prompt_handler[key_string] = false
|
249
|
+
raise e
|
250
|
+
end
|
251
|
+
|
252
|
+
return value
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
value = findProperty.call(key)
|
257
|
+
else
|
258
|
+
findProperty = lambda do |keys|
|
259
|
+
return getNestedProperty(keys)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
if value == nil && key.size == 1 && (host = getNestedProperty([DEPLOYMENT_HOST]))
|
264
|
+
value = findProperty.call([HOSTS, host, key[0]])
|
265
|
+
|
266
|
+
if value == nil && key.size == 1
|
267
|
+
dataservice = getProperty(DEPLOYMENT_DATASERVICE)
|
268
|
+
|
269
|
+
if dataservice
|
270
|
+
value = findProperty.call([DATASERVICES, dataservice, key[0]])
|
271
|
+
|
272
|
+
if value == nil
|
273
|
+
value = findProperty.call([REPL_SERVICES, dataservice + "_" + host, key[0]])
|
274
|
+
end
|
275
|
+
|
276
|
+
if value == nil
|
277
|
+
value = findProperty.call([MANAGERS, dataservice + "_" + host, key[0]])
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
if value == nil
|
283
|
+
value = findProperty.call([CONNECTORS, host, key[0]])
|
284
|
+
end
|
285
|
+
|
286
|
+
if value == nil && key.size == 1 && (svc = getNestedProperty([DEPLOYMENT_SERVICE]))
|
287
|
+
value = findProperty.call([REPL_SERVICES, svc, key[0]])
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
value
|
292
|
+
end
|
293
|
+
|
294
|
+
# Get the config file value for a property.
|
295
|
+
def getTemplateValue(key, transform_values_method = nil)
|
296
|
+
if transform_values_method == nil
|
297
|
+
transform_values_method = method(:blank_transform_values_method)
|
298
|
+
end
|
299
|
+
|
300
|
+
if key.is_a?(String)
|
301
|
+
key_string = key
|
302
|
+
key = key.split('.')
|
303
|
+
else
|
304
|
+
key_string = key.join('.')
|
305
|
+
end
|
306
|
+
|
307
|
+
if usePromptHandler()
|
308
|
+
findProperty = lambda do |keys|
|
309
|
+
if @in_template_value_prompt_handler[key_string] == true
|
310
|
+
return nil
|
311
|
+
end
|
312
|
+
|
313
|
+
begin
|
314
|
+
@in_template_value_prompt_handler[key_string] = true
|
315
|
+
|
316
|
+
value = getPromptHandler().find_template_value(keys, transform_values_method)
|
317
|
+
|
318
|
+
@in_template_value_prompt_handler[key_string] = false
|
319
|
+
rescue IgnoreError
|
320
|
+
@in_template_value_prompt_handler[key_string] = false
|
321
|
+
rescue => e
|
322
|
+
@in_template_value_prompt_handler[key_string] = false
|
323
|
+
raise e
|
324
|
+
end
|
325
|
+
|
326
|
+
return value
|
327
|
+
end
|
328
|
+
|
329
|
+
value = findProperty.call(key)
|
330
|
+
end
|
331
|
+
|
332
|
+
if value == nil && key.size == 1 && (host = getNestedProperty([DEPLOYMENT_HOST]))
|
333
|
+
value = findProperty.call([HOSTS, host, key[0]])
|
334
|
+
|
335
|
+
if value == nil && key.size == 1
|
336
|
+
dataservice = getNestedProperty([DEPLOYMENT_DATASERVICE])
|
337
|
+
if dataservice == nil
|
338
|
+
dataservice = getProperty(DEPLOYMENT_DATASERVICE)
|
339
|
+
end
|
340
|
+
|
341
|
+
if dataservice
|
342
|
+
value = findProperty.call([DATASERVICES, dataservice, key[0]])
|
343
|
+
|
344
|
+
if value == nil
|
345
|
+
value = findProperty.call([REPL_SERVICES, dataservice + "_" + host, key[0]])
|
346
|
+
end
|
347
|
+
|
348
|
+
if value == nil
|
349
|
+
value = findProperty.call([MANAGERS, dataservice + "_" + host, key[0]])
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
if value == nil
|
355
|
+
value = findProperty.call([CONNECTORS, host, key[0]])
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
if value == nil && key.size == 1 && (svc = getNestedProperty([DEPLOYMENT_SERVICE]))
|
360
|
+
value = findProperty.call([REPL_SERVICES, svc, key[0]])
|
361
|
+
end
|
362
|
+
|
363
|
+
value
|
364
|
+
end
|
365
|
+
|
366
|
+
def blank_transform_values_method(matches)
|
367
|
+
""
|
368
|
+
end
|
369
|
+
|
370
|
+
# Get the property value or return the default if nil
|
371
|
+
def getPropertyOr(key, default = "")
|
372
|
+
value = getProperty(key)
|
373
|
+
if value == nil
|
374
|
+
default
|
375
|
+
else
|
376
|
+
value
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def getNestedPropertyOr(key, default = "")
|
381
|
+
value = getNestedProperty(key)
|
382
|
+
if value == nil
|
383
|
+
default
|
384
|
+
else
|
385
|
+
value
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
# Set a property value.
|
390
|
+
def setProperty(key, value)
|
391
|
+
if key.is_a?(String)
|
392
|
+
key = key.split('.')
|
393
|
+
end
|
394
|
+
|
395
|
+
setNestedProperty(value, key)
|
396
|
+
end
|
397
|
+
|
398
|
+
# Set the property to a value only if it is currently unset.
|
399
|
+
def setDefault(key, value)
|
400
|
+
if key.is_a?(String)
|
401
|
+
key = key.split('.')
|
402
|
+
end
|
403
|
+
|
404
|
+
if getNestedProperty(key) == nil
|
405
|
+
setNestedProperty(value, key)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
# Set multiple properties from a delimited string of key value pairs.
|
410
|
+
def setPropertiesFromList(list, delimiter)
|
411
|
+
keyValuePairs = list.split(delimiter)
|
412
|
+
keyValuePairs.each do |pair|
|
413
|
+
if pair =~ /^(.*)=(.*)$/
|
414
|
+
key = $1
|
415
|
+
value = $2
|
416
|
+
setProperty(key, value)
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
# Get the underlying hash table.
|
422
|
+
def hash()
|
423
|
+
@props
|
424
|
+
end
|
425
|
+
|
426
|
+
def usePromptHandler
|
427
|
+
return @use_prompt_handler
|
428
|
+
end
|
429
|
+
|
430
|
+
def getPromptHandler
|
431
|
+
unless usePromptHandler()
|
432
|
+
return nil
|
433
|
+
end
|
434
|
+
|
435
|
+
unless @prompt_handler
|
436
|
+
@prompt_handler = ConfigurePromptHandler.new(self)
|
437
|
+
end
|
438
|
+
|
439
|
+
return @prompt_handler
|
440
|
+
end
|
441
|
+
|
442
|
+
def empty?
|
443
|
+
(@props.size() == 0)
|
444
|
+
end
|
445
|
+
|
446
|
+
def override(key, value = {})
|
447
|
+
if value == nil || value == ""
|
448
|
+
value = {}
|
449
|
+
end
|
450
|
+
|
451
|
+
setProperty(key, getNestedPropertyOr(key, {}).merge(value))
|
452
|
+
end
|
453
|
+
|
454
|
+
def include(key, value = {})
|
455
|
+
if value == nil || value == ""
|
456
|
+
value = {}
|
457
|
+
end
|
458
|
+
|
459
|
+
setProperty(key, value.merge(getNestedPropertyOr(key, {})))
|
460
|
+
end
|
461
|
+
|
462
|
+
def append(key, value = [])
|
463
|
+
if value == nil || value == ""
|
464
|
+
value = []
|
465
|
+
end
|
466
|
+
|
467
|
+
if ! value.kind_of?(Array)
|
468
|
+
value=Array(value)
|
469
|
+
end
|
470
|
+
currentvalue=getNestedPropertyOr(key, [])
|
471
|
+
if ! currentvalue.kind_of?(Array)
|
472
|
+
currentvalue=Array(currentvalue)
|
473
|
+
end
|
474
|
+
setProperty(key, (currentvalue+value).uniq())
|
475
|
+
end
|
476
|
+
end
|