right_infrastructure_agent 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ca12e04c5b64e16c354a7031c39599a36d0c431d
4
+ data.tar.gz: 463c200718bfe3c8f01817497764942c2a44b173
5
+ SHA512:
6
+ metadata.gz: 90d0a95a758ecc476cb5b78069e4a9914a1f4f61671855a9bb7f0217254a1b46c1b494bf52a9e35a6bae1a0ed01a3832a16d4f84b06f25ed8751dfe7ef8bcd69
7
+ data.tar.gz: 51a45fc9db03ee26f6e0b1b6d536e1e3060ff49f2682cf00565c4f17f63b7b9c4f28582276e2f3f8936646de8cb19ae763e6066b3d5c08c62a320510c4326299
data/LICENSE ADDED
@@ -0,0 +1,10 @@
1
+ # Copyright (c) 2009-2011 RightScale, Inc, All Rights Reserved Worldwide.
2
+ #
3
+ # THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE
4
+ # AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use,
5
+ # reproduction, modification, or disclosure of this program is
6
+ # strictly prohibited. Any use of this program by an authorized
7
+ # licensee is strictly subject to the terms and conditions,
8
+ # including confidentiality obligations, set forth in the applicable
9
+ # License Agreement between RightScale.com, Inc. and
10
+ # the licensee.
data/README.rdoc ADDED
@@ -0,0 +1,65 @@
1
+ = RightInfrastructureAgent
2
+
3
+ = DESCRIPTION
4
+
5
+ == Synopsis
6
+
7
+ RightInfrastructureAgent provides the foundation for RightScale infrastructure
8
+ servers that connect into the RightScale system via the RabbitMQ message bus.
9
+ It extends RightAgent in configuration, monitoring, and packet handling areas
10
+ as needed generically by infrastructure servers.
11
+
12
+ Maintained by the RightScale Teal Team
13
+
14
+ == Interface
15
+
16
+ A RightInfrastructureAgent exposes its services via actors and methods that are
17
+ invoked by requests packets it receives via its message queue. The actors
18
+ provided are specific to the given agent.
19
+
20
+ RightInfrastructureAgent comes with several library modules for forming basic
21
+ command line tools in addition to those provided in RightAgent:
22
+
23
+ * <b>infrastructure_agent_deployer</b>: Build configuration file for running an agent (rad tool)
24
+ * <b>infrastructure_agent_controller</b>: Manage an agent that has been configured (rnac tool)
25
+
26
+ == Supported Configuration
27
+
28
+ RightInfrastructureAgent has been tested on EC2 instances running CentOS 5.2 and Ubuntu 8.10.
29
+
30
+ == Work in Progress
31
+
32
+ RightInfrastructureAgent is work in progress.
33
+
34
+ = ADDITIONAL RESOURCES
35
+
36
+ * [1] RabbitMQ is http://www.rabbitmq.com/documentation.html
37
+
38
+ = CHANGELOG
39
+
40
+ * 0.15.1: The server_superuser role is now taken into account when building policies
41
+
42
+ = LICENSE
43
+
44
+ <b>RightAgent</b>
45
+
46
+ Copyright:: Copyright (c) 2011 RightScale, Inc.
47
+
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of this software and associated documentation files (the
50
+ 'Software'), to deal in the Software without restriction, including
51
+ without limitation the rights to use, copy, modify, merge, publish,
52
+ distribute, sublicense, and/or sell copies of the Software, and to
53
+ permit persons to whom the Software is furnished to do so, subject to
54
+ the following conditions:
55
+
56
+ The above copyright notice and this permission notice shall be
57
+ included in all copies or substantial portions of the Software.
58
+
59
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
60
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
61
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
62
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
63
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
64
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
65
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,86 @@
1
+ #-- -*-ruby-*-
2
+ # Copyright: Copyright (c) 2010 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'rubygems'
25
+ require 'bundler/setup'
26
+ require 'fileutils'
27
+ require 'rake'
28
+ require 'rspec/core/rake_task'
29
+ require 'rdoc/task'
30
+ require 'rake/gempackagetask'
31
+ require 'rake/clean'
32
+
33
+ task :default => 'spec'
34
+
35
+ # == Gem packaging == #
36
+
37
+ desc "Package gem"
38
+ gemtask = Rake::GemPackageTask.new(Gem::Specification.load("right_infrastructure_agent.gemspec")) do |package|
39
+ package.package_dir = ENV['PACKAGE_DIR'] || 'pkg'
40
+ package.need_zip = true
41
+ package.need_tar = true
42
+ end
43
+
44
+ directory gemtask.package_dir
45
+
46
+ CLEAN.include(gemtask.package_dir)
47
+
48
+ # == Unit tests == #
49
+
50
+ RSPEC_OPTS = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
51
+
52
+ desc 'Run unit tests'
53
+ RSpec::Core::RakeTask.new do |t|
54
+ t.rspec_opts = RSPEC_OPTS
55
+ end
56
+
57
+ namespace :spec do
58
+ desc 'Run unit tests with RCov'
59
+ RSpec::Core::RakeTask.new(:rcov) do |t|
60
+ t.rspec_opts = RSPEC_OPTS
61
+ t.rcov = true
62
+ t.rcov_opts = %q[--exclude "spec"]
63
+ end
64
+
65
+ desc 'Print Specdoc for all unit tests'
66
+ RSpec::Core::RakeTask.new(:doc) do |t|
67
+ t.rspec_opts = ["--format", "documentation"]
68
+ end
69
+ end
70
+
71
+ # == Documentation == #
72
+
73
+ desc 'Generate API documentation to doc/rdocs/index.html'
74
+ Rake::RDocTask.new do |rd|
75
+ rd.rdoc_dir = 'doc/rdocs'
76
+ rd.main = 'README.rdoc'
77
+ rd.rdoc_files.include 'README.rdoc', 'lib/**/*.rb'
78
+ end
79
+ CLEAN.include('doc/rdocs')
80
+
81
+ # == Emacs integration ==
82
+
83
+ desc 'Rebuild TAGS file for emacs'
84
+ task :tags do
85
+ sh 'rtags -R lib spec'
86
+ end
@@ -0,0 +1,26 @@
1
+ # Copyright (c) 2009-2011 RightScale, Inc, All Rights Reserved Worldwide.
2
+ #
3
+ # THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE
4
+ # AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use,
5
+ # reproduction, modification, or disclosure of this program is
6
+ # strictly prohibited. Any use of this program by an authorized
7
+ # licensee is strictly subject to the terms and conditions,
8
+ # including confidentiality obligations, set forth in the applicable
9
+ # License Agreement between RightScale.com, Inc. and the licensee.
10
+
11
+ require 'rubygems'
12
+ require 'right_agent'
13
+
14
+ unless defined?(RIGHT_INFRASTRUCTURE_AGENT_BASE_DIR)
15
+ RIGHT_INFRASTRUCTURE_AGENT_BASE_DIR =
16
+ File.normalize_path(File.join(File.dirname(__FILE__), 'right_infrastructure_agent'))
17
+ end
18
+
19
+ require File.normalize_path(File.join(RIGHT_INFRASTRUCTURE_AGENT_BASE_DIR, 'models_helper'))
20
+ require File.normalize_path(File.join(RIGHT_INFRASTRUCTURE_AGENT_BASE_DIR, 'login_policy_factory'))
21
+ require File.normalize_path(File.join(RIGHT_INFRASTRUCTURE_AGENT_BASE_DIR, 'infrastructure_helpers'))
22
+ require File.normalize_path(File.join(RIGHT_INFRASTRUCTURE_AGENT_BASE_DIR, 'infrastructure_auth_client'))
23
+ require File.normalize_path(File.join(RIGHT_INFRASTRUCTURE_AGENT_BASE_DIR, 'rainbows_agent_controller'))
24
+ require File.normalize_path(File.join(RIGHT_INFRASTRUCTURE_AGENT_BASE_DIR, 'global_object_replicator_sink'))
25
+ require File.normalize_path(File.join(RIGHT_INFRASTRUCTURE_AGENT_BASE_DIR, 'global_object_replicator_source'))
26
+ require File.normalize_path(File.join(RIGHT_INFRASTRUCTURE_AGENT_BASE_DIR, 'command_constants'))
@@ -0,0 +1,34 @@
1
+ #
2
+ # Copyright (c) 2009-2014 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ module RightScale
25
+
26
+ # Extend this class to support infrastructure agents
27
+ class CommandConstants
28
+
29
+ # Ports used for command protocol
30
+ BASE_TESTER_AGENT_SOCKET_PORT = 69000
31
+ BASE_ROUTER_SOCKET_PORT = 79000
32
+
33
+ end
34
+ end
@@ -0,0 +1,337 @@
1
+ #--
2
+ # Copyright (c) 2011-2013 RightScale, Inc, All Rights Reserved Worldwide.
3
+ #
4
+ # THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE
5
+ # AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use,
6
+ # reproduction, modification, or disclosure of this program is
7
+ # strictly prohibited. Any use of this program by an authorized
8
+ # licensee is strictly subject to the terms and conditions,
9
+ # including confidentiality obligations, set forth in the applicable
10
+ # License Agreement between RightScale.com, Inc. and
11
+ # the licensee.
12
+ #++
13
+
14
+ module RightScale
15
+
16
+ # This module is for use as a mixin in an HTTP controller that handles requests from
17
+ # a global object replication source (e.g., library) to synchronize its replicas.
18
+ #
19
+ # This module expects the following to be defined:
20
+ # - logger - variable pointing to the standard logger in use
21
+ # - global_object_replicator_sink - method returning type of replication sink, e.g., 'core'
22
+ module GlobalObjectReplicatorSink
23
+
24
+ include RightScale::InfrastructureHelpers
25
+ include RightScale::ModelsHelper
26
+
27
+ # Update the specified right_site core object (class_name, id) so that its attributes
28
+ # match those of the global object
29
+ #
30
+ # @param :source [String] Replication source, e.g., 'library' or 'wasabi'
31
+ # @param :class_name [String] Name of the object class being updated
32
+ # @param :id [Integer] Unique identifier of the object being updated
33
+ # @param :schema_version [Integer] Schema version of the model (class)
34
+ # @param :global_object_version [String] Global object version of the model (class)
35
+ # @param :attrs [String] All attributes defined for the object represented by :class_name
36
+ # and :id in yaml
37
+ #
38
+ # @return [NilClass] nil
39
+ #
40
+ # @raise [RightScale::Exceptions::QueryFailure] Failed to update object
41
+ # @raise [RightScale::Exceptions::RetryableError] Query failed but may be retried
42
+ def handle_global_object_change
43
+ source = params[:source]
44
+ if (object_class = replicate_sink_class(params[:class_name]))
45
+ logger.info("GlobalObjectReplica: Processing #{source} global object change for #{object_class.name}/#{params[:id]}")
46
+
47
+ id = to_int_or_nil(params[:id])
48
+ schema_version = to_int_or_nil(params[:schema_version])
49
+ action = "replicator sink handle_global_object_change for #{object_class.name}/#{id} " +
50
+ "(schema version #{schema_version}, global object version #{params[:global_object_version]})"
51
+ success = query(action, :include_backtrace_in_last_error => true) do
52
+ object_class.handle_global_object_change(id, schema_version, params[:global_object_version], params[:attrs])
53
+ true
54
+ end
55
+
56
+ raise RightScale::Exceptions::QueryFailure.new(@last_error) unless success
57
+ else
58
+ logger.warn("GlobalObjectReplica: Ignoring replicator sink handle_global_object_change from #{source} " +
59
+ "for unknown will_replicate sink class #{params[:class_name].inspect}")
60
+ end
61
+ render_nothing
62
+ end
63
+
64
+ # Compare checksum parameter of a replicated table and, if they do not match,
65
+ # begin the synchronization process by sending verify_replica_range to replicator source
66
+ #
67
+ # @param :source [String] Replication source, e.g., 'library' or 'wasabi'
68
+ # @param :class_name [String] Name of a class that acts_as_global_object_replica
69
+ # @param :checksum_type [String] Type of checksum: only 'global_object_version_sum'
70
+ # @param :checksum_value [Integer, NilClass] Sum of global_object_versions for the table
71
+ # in the source database
72
+ #
73
+ # @return [NilClass] nil
74
+ #
75
+ # @raise [ArgumentError] Unknown replication source
76
+ # @raise [RightScale::Exceptions::QueryFailure] Failed to complete verify replicas query
77
+ # @raise [RightScale::Exceptions::RetryableError] Query failed but may be retried
78
+ def verify_replicas
79
+ source = params[:source]
80
+ if (replica_class = replicate_sink_class(params[:class_name]))
81
+ checksum_type = params[:checksum_type]
82
+ checksum_value = params[:checksum_value]
83
+
84
+ success = query("verify_replicas for #{replica_class.name}", :email_errors => true) do
85
+ if replica_class.calculate_global_object_checksum(checksum_type) == checksum_value
86
+ logger.info("GlobalObjectReplica: Verified #{source} #{replica_class.name} global_object_version_sum.")
87
+ set_global_object_replication_status(replica_class.name, checksum_type, :completed => true)
88
+ else
89
+ logger.info("GlobalObjectReplica: Verification of #{source} #{replica_class.name} global_object_version_sum failed. " +
90
+ "Beginning synchronization.")
91
+ set_global_object_replication_status(replica_class.name, checksum_type, :start => true)
92
+
93
+ max_id = replica_class.max_id
94
+ verify_next_replica_range(source, replica_class, checksum_type, max_id, 0, max_id)
95
+ end
96
+ true
97
+ end
98
+
99
+ unless success
100
+ set_global_object_replication_status(replica_class.name, checksum_type, :failed => true)
101
+ raise RightScale::Exceptions::QueryFailure.new(@last_error)
102
+ end
103
+ else
104
+ logger.warn("GlobalObjectReplica: Ignoring replicator sink verify_replicas from #{source} " +
105
+ "for unknown will_replicate sink class #{params[:class_name].inspect}")
106
+ end
107
+ render_nothing
108
+ end
109
+
110
+ # Update rows using any received synchronization data and send a
111
+ # verify_replica_range request for the next range to process
112
+ #
113
+ # @param :source [String] Replication source, e.g., 'library' or 'wasabi'
114
+ # @param :class_name [String] Name of a class that acts_as_global_object_replica
115
+ # @param :checksum_type [String] Type of checksum: only 'global_object_version_sum'
116
+ # @param :max_id_at_start [Integer] Max ID of the replica_class when first
117
+ # started synchronization process
118
+ # @param :begin_id [Integer, NilClass] First row ID in range verified by the source,
119
+ # or nil for all rows
120
+ # @param :end_id [Integer, NilClass] Last row ID in range verified by the source,
121
+ # or nil for all rows
122
+ # @param :checksum_matched [Boolean] Whether checksum matched
123
+ # @param :records_to_synchronize [Array, NilClass] Blank array or nil (if the last
124
+ # verification check was positive) or an array of hashes to be used to update the
125
+ # replicated table with each hash containing the keys used in handle_global_object_change:
126
+ # :id, :schema_version, :global_object_version, :attrs
127
+ # @param :has_more [Boolean] Indicates at least one more range should be verified
128
+ #
129
+ # @return [NilClass] nil
130
+ #
131
+ # @raise [ArgumentError] Unknown replication source
132
+ # @raise [RightScale::Exceptions::QueryFailure] Failed to complete synchronization query
133
+ # @raise [RightScale::Exceptions::RetryableError] Query failed but may be retried
134
+ def synchronize_replica_range
135
+ source = params[:source]
136
+ if (replica_class = replicate_sink_class(params[:class_name]))
137
+ checksum_type = params[:checksum_type]
138
+ max_id_at_start = to_int_or_nil(params[:max_id_at_start])
139
+ begin_id = to_int_or_nil(params[:begin_id])
140
+ end_id = to_int_or_nil(params[:end_id])
141
+ checksum_matched = to_bool(params[:checksum_matched])
142
+ records = params[:records_to_synchronize] || []
143
+ has_more = to_bool(params[:has_more])
144
+
145
+ success = query("synchronize_replica_range for #{replica_class.name}", :email_errors => true) do
146
+ logger.info("GlobalObjectReplica: Synchronizing #{source} #{replica_class.name} range #{begin_id}-#{end_id} " +
147
+ "(#{checksum_type} #{checksum_matched ? 'match' : 'mismatch'}) #{records.size} rows received.")
148
+ # Shuffle the records here so that collisions are less likely if two processes are trying to sync at once
149
+ records.shuffle.each do |h|
150
+ h = RightScale::SerializationHelper.symbolize_keys(h)
151
+ replica_class.handle_global_object_change(h[:id], h[:schema_version], h[:global_object_version], h[:attrs])
152
+ end
153
+
154
+ in_sync = checksum_matched || !(records.nil? || records.empty?)
155
+
156
+ if has_more || !in_sync
157
+ set_global_object_replication_status(replica_class.name, checksum_type, :percent_complete =>
158
+ (100 * ([begin_id, 1].max / [max_id_at_start, 1].max.to_f)).floor) if in_sync
159
+
160
+ next_begin_id, next_end_id = calculate_next_range_for_binary_sync(source, max_id_at_start,
161
+ replica_class.will_replicate_initialization_chunk_size,
162
+ begin_id, end_id, in_sync)
163
+ verify_next_replica_range(source, replica_class, checksum_type, max_id_at_start, next_begin_id, next_end_id)
164
+ else
165
+ logger.info("GlobalObjectReplica: Synchronization of #{source} #{replica_class.name} complete at row #{end_id}")
166
+ set_global_object_replication_status(replica_class.name, checksum_type, :completed => true)
167
+ end
168
+ true
169
+ end
170
+
171
+ unless success
172
+ set_global_object_replication_status(replica_class.name, checksum_type, :failed => true)
173
+ raise RightScale::Exceptions::QueryFailure.new(@last_error)
174
+ end
175
+ else
176
+ logger.warn("GlobalObjectReplica: Ignoring replicator sink synchronize_replica_range from #{source} " +
177
+ "for unknown will_replicate sink class #{params[:class_name].inspect}")
178
+ end
179
+ render_nothing
180
+ end
181
+
182
+ protected
183
+
184
+ # Calculate the checksum for the next synchronization range and send to the replication source
185
+ #
186
+ # @param :source [String] Replication source, e.g., 'library' or 'wasabi'
187
+ # @param replica_class [Class] Class that acts_as_global_object_replica
188
+ # @param checksum_type [String] Type of checksum to perform: only 'global_object_version_sum'
189
+ # @param max_id_at_start [Integer] Max ID of the replica_class when first
190
+ # started synchronization process
191
+ # @param begin_id [Integer, NilClass] First ID in the checksum range, or nil for all rows
192
+ # @param end_id [Integer, NilClass] Last ID in the checksum range, or nil for all rows
193
+ #
194
+ # @return [TrueClass] Always return true
195
+ #
196
+ # @raise [ArgumentError] Unknown replication source
197
+ def verify_next_replica_range(source, replica_class, checksum_type, max_id_at_start, begin_id, end_id)
198
+ replicator = case source # TODO Replace this with "#{source}_replicator" once all replicator actors gone
199
+ when "library" then "library_replicator"
200
+ when "wasabi" then "wasabi_replicator_source"
201
+ else raise ArgumentError, "Unknown replication source: #{source.inspect}"
202
+ end
203
+ payload = {
204
+ :sink => global_object_replicator_sink,
205
+ :class_name => replica_class.name,
206
+ :schema_version => replica_class.will_replicate_current_schema_version,
207
+ :checksum_type => checksum_type,
208
+ :max_id_at_start => max_id_at_start,
209
+ :begin_id => begin_id,
210
+ :end_id => end_id,
211
+ :send_records_on_checksum_mismatch => calc_size(begin_id, end_id) <= replica_class.will_replicate_initialization_chunk_size,
212
+ :checksum_value => replica_class.calculate_global_object_checksum(checksum_type, begin_id, end_id),
213
+ # Assume global if this sink has no associated shard
214
+ :shard_id => (RightSharding::ShardHandler.fetch_this_shard_id(SystemConfig) rescue RightScale::Packet::GLOBAL) }
215
+
216
+ EM.next_tick do
217
+ # Execute this request on next_tick since, depending on the configuration, the router could route
218
+ # this request directly via HTTP and there would be no break in the chain of replication requests
219
+ begin
220
+ RightScale::RightHttpClient.push("/#{replicator}/verify_replica_range", payload)
221
+ rescue Exception => e
222
+ logger.error(format_error("Failed to verify_replica_range for class #{replica_class.name}", e))
223
+ end
224
+ end
225
+ true
226
+ end
227
+
228
+ # Calculate the begin_id and end_id for the next range to compare given the current range & status
229
+ # Basically successive calls to this method will result in a binary depth-first search finding out
230
+ # of sync ranges by using the following rules:
231
+ # 1. If the current range is not in_sync, return the left half as the next range
232
+ # 2. If the current range is a left half (of some greater range) and it is in_sync, return the
233
+ # corresponding right half as the next range
234
+ # 3. If the current range is a right half and it is in_sync, move it up until it is part of a left
235
+ # half, then return the corresponding right half as the next range.
236
+ #
237
+ # @param max_id [Integer] Max ID when the search started, which is needed to calculate the left/right
238
+ # halves consistently during the whole search
239
+ # @param min_size [Integer] Minimum chunk size that is used to retrieve records
240
+ # @param last_begin_id [Integer] First ID in the current checksum range
241
+ # @param last_end_id [Integer] Last ID in the current checksum range
242
+ # @param in_sync [Boolean] true if the current checksum range is synchronized, i.e.,
243
+ # if do not need to go deeper here, otherwise false
244
+ #
245
+ # @return [Array] Next range to search in the form [next_begin_id, next_end_id]
246
+ #
247
+ # @raise [StandardError] Unexpected state
248
+ def calculate_next_range_for_binary_sync(source, max_id, min_size, last_begin_id, last_end_id, in_sync)
249
+ last_size, last_center = calc_size_and_center(last_begin_id, last_end_id)
250
+
251
+ next_begin_id = nil
252
+ next_end_id = nil
253
+
254
+ if !in_sync
255
+ if last_size <= min_size
256
+ raise StandardError.new("Unexpected state: Not in sync, but last_size #{last_size} is less " +
257
+ "then min_size #{min_size}. This shouldn't happen because should have received sync records " +
258
+ "from the #{source} database and applied them.")
259
+ else
260
+ # Check the left half next (i.e. depth-first search)
261
+ next_begin_id = last_begin_id
262
+ next_end_id = last_center
263
+ end
264
+ else
265
+ if last_end_id >= max_id
266
+ # Past the end, so just do the next initialization chunk size
267
+ next_begin_id = last_end_id + 1
268
+ next_end_id = last_end_id + min_size
269
+ else
270
+ # In sync in the last range, so figure out current location in the depth first search and
271
+ # check the next right branch
272
+ cur_begin_id = 0
273
+ cur_end_id = max_id
274
+ cur_size, cur_center = calc_size_and_center(cur_begin_id, cur_end_id)
275
+
276
+ while cur_size > last_size
277
+ if last_center < cur_center
278
+ # About to move down a left branch, so save the corresponding right branch and
279
+ # if don't go down anymore lefts, this is the branch to sync next
280
+ next_begin_id = cur_center + 1
281
+ next_end_id = cur_end_id
282
+
283
+ cur_end_id = cur_center
284
+ else
285
+ cur_begin_id = cur_center + 1
286
+ end
287
+
288
+ cur_size, cur_center = calc_size_and_center(cur_begin_id, cur_end_id)
289
+ end
290
+ end
291
+ end
292
+
293
+ if next_begin_id.nil? || next_end_id.nil?
294
+ raise StandardError.new("Unexpected state: Nil result when calculating next range for binary sync: max_id=#{max_id}, " +
295
+ "min_size=#{min_size}, last_begin=#{last_begin_id}, last_end=#{last_end_id}, in_sync=#{in_sync}")
296
+ end
297
+
298
+ [next_begin_id, next_end_id]
299
+ end
300
+
301
+ def calc_size(begin_id, end_id)
302
+ end_id - begin_id + 1
303
+ end
304
+
305
+ def calc_size_and_center(begin_id, end_id)
306
+ [calc_size(begin_id, end_id), ((begin_id + end_id) / 2)]
307
+ end
308
+
309
+ def replicate_sink_class(class_name)
310
+ klass = constantize(class_name) rescue nil
311
+ (klass.respond_to?(:will_replicate_sink?) && klass.will_replicate_sink?) ? klass : nil
312
+ end
313
+
314
+ def set_global_object_replication_status(class_name, checksum_type, options = {})
315
+ status = GlobalObjectReplicationStatus.find_or_initialize_by_name(class_name)
316
+ status.last_sync_at = Time.now
317
+ status.last_sync_status = if options[:failed]
318
+ status.last_sync_status = "failed" + status.last_sync_status.sub("in progress", "")
319
+ elsif options[:completed]
320
+ 'completed'
321
+ elsif options[:start]
322
+ "in progress 0%"
323
+ elsif options[:percent_complete] && options[:percent_complete] > 100
324
+ "in progress unknown %"
325
+ else
326
+ "in progress #{options[:percent_complete]}%"
327
+ end
328
+ status.last_sync_checksum_type = checksum_type
329
+ status.last_sync_start_at = Time.now if options[:start]
330
+ status.save!
331
+ rescue Exception => e
332
+ logger.error(format_error("Failed to update global object replication status", e, :trace))
333
+ end
334
+
335
+ end # GlobalObjectReplicatorSink
336
+
337
+ end # RightScale