right_infrastructure_agent 1.1.2

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 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