jetpants 0.7.8 → 0.7.10

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.
@@ -87,6 +87,15 @@ module Jetpants
87
87
  raise "Plugin must override Topology#count_spares"
88
88
  end
89
89
 
90
+ # Returns a list of valid role symbols in use in Jetpants.
91
+ def valid_roles
92
+ [:master, :active_slave, :standby_slave, :backup_slave]
93
+ end
94
+
95
+ # Returns a list of valid role symbols which indicate a slave status
96
+ def slave_roles
97
+ valid_roles.reject {|r| r == :master}
98
+ end
90
99
 
91
100
  ###### Instance Methods ####################################################
92
101
 
@@ -144,6 +153,19 @@ module Jetpants
144
153
  claim_spares(1, options)[0]
145
154
  end
146
155
 
156
+ # Returns if the supplied role is valid
157
+ def valid_role? role
158
+ valid_roles.include? role.to_s.downcase.to_sym
159
+ end
160
+
161
+ # Converts the supplied roles (strings or symbols) into lowercase symbol versions
162
+ # Will expand out special role of :slave to be all slave roles.
163
+ def normalize_roles(*roles)
164
+ roles = roles.flatten.map {|r| r.to_s.downcase == 'slave' ? slave_roles.map(&:to_s) : r.to_s.downcase}.flatten
165
+ roles.each {|r| raise "#{r} is not a valid role" unless valid_role? r}
166
+ roles.uniq.map &:to_sym
167
+ end
168
+
147
169
  synchronized
148
170
  # Clears the pool list and nukes cached DB and Host object lookup tables
149
171
  def clear
@@ -0,0 +1,77 @@
1
+ # Adds conversion methods to the Collins::Asset class for obtaining Jetpants equivalents
2
+
3
+ module Collins
4
+ class Asset
5
+
6
+ # Convert a Collins::Asset to a Jetpants::DB. Requires asset TYPE to be SERVER_NODE.
7
+ def to_db
8
+ raise "Can only call to_db on SERVER_NODE assets, but #{self} has type #{type}" unless type.upcase == 'SERVER_NODE'
9
+ backend_ip_address.to_db
10
+ end
11
+
12
+
13
+ # Convert a Collins::Asset to a Jetpants::Host. Requires asset TYPE to be SERVER_NODE.
14
+ def to_host
15
+ raise "Can only call to_host on SERVER_NODE assets, but #{self} has type #{type}" unless type.upcase == 'SERVER_NODE'
16
+ backend_ip_address.to_host
17
+ end
18
+
19
+
20
+ # Convert a Collins::Asset to either a Jetpants::Pool or a Jetpants::Shard, depending
21
+ # on the value of PRIMARY_ROLE. Requires asset TYPE to be CONFIGURATION.
22
+ def to_pool
23
+ raise "Can only call to_pool on CONFIGURATION assets, but #{self} has type #{type}" unless type.upcase == 'CONFIGURATION'
24
+ raise "Unknown primary role #{primary_role} for configuration asset #{self}" unless ['MYSQL_POOL', 'MYSQL_SHARD'].include?(primary_role.upcase)
25
+ raise "No pool attribute set on asset #{self}" unless pool && pool.length > 0
26
+
27
+ # Find the master(s) for this pool. If we got back multiple masters, first
28
+ # try ignoring the remote datacenter ones
29
+ master_assets = Jetpants.topology.server_node_assets(pool.downcase, :master)
30
+ if master_assets.count > 1
31
+ results = master_assets.select {|a| a.location.nil? || a.location.upcase == Plugin::JetCollins.datacenter}
32
+ master_assets = results if results.count > 0
33
+ end
34
+ puts "WARNING: multiple masters found for pool #{pool}; using first match" if master_assets.count > 1
35
+
36
+ if master_assets.count == 0
37
+ puts "WARNING: no masters found for pool #{pool}; ignoring pool entirely"
38
+ result = nil
39
+
40
+ elsif primary_role.upcase == 'MYSQL_POOL'
41
+ result = Jetpants::Pool.new(pool.downcase, master_assets.first.to_db)
42
+ if aliases
43
+ aliases.split(',').each {|a| result.add_alias(a.downcase)}
44
+ end
45
+ result.slave_name = slave_pool_name if slave_pool_name
46
+ result.master_read_weight = master_read_weight if master_read_weight
47
+
48
+ # We intentionally only look for active slaves in the current datacenter, since we
49
+ # treat other datacenters' slaves as backup slaves to prevent promotion or cross-DC usage
50
+ active_slave_assets = Jetpants.topology.server_node_assets(pool.downcase, :active_slave)
51
+ active_slave_assets.reject! {|a| a.location && a.location.upcase != Plugin::JetCollins.datacenter}
52
+ active_slave_assets.each do |asset|
53
+ weight = asset.slave_weight && asset.slave_weight.to_i > 0 ? asset.slave_weight.to_i : 100
54
+ result.has_active_slave(asset.to_db, weight)
55
+ end
56
+
57
+ elsif primary_role.upcase == 'MYSQL_SHARD'
58
+ result = Jetpants::Shard.new(shard_min_id.to_i,
59
+ shard_max_id == 'INFINITY' ? 'INFINITY' : shard_max_id.to_i,
60
+ master_assets.first.to_db,
61
+ shard_state.downcase.to_sym)
62
+
63
+ # We'll need to set up the parent/child relationship if a shard split is in progress,
64
+ # BUT we need to wait to do that later since the shards may have been returned by
65
+ # Collins out-of-order, so the parent shard object might not exist yet.
66
+ # For now we just remember the NAME of the parent shard.
67
+ result.has_parent = shard_parent if shard_parent
68
+
69
+ else
70
+ raise "Unknown configuration asset primary role #{primary_role} for asset #{self}"
71
+ end
72
+
73
+ result
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,77 @@
1
+ # JetCollins monkeypatches to add Collins integration
2
+
3
+ module Jetpants
4
+ class DB
5
+
6
+ ##### JETCOLLINS MIX-IN ####################################################
7
+
8
+ include Plugin::JetCollins
9
+
10
+ collins_attr_accessor :slave_weight
11
+
12
+ # Because we only support 1 mysql instance per machine for now, we can just
13
+ # delegate this over to the host
14
+ def collins_asset
15
+ @host.collins_asset
16
+ end
17
+
18
+
19
+ ##### METHOD OVERRIDES #####################################################
20
+
21
+ # Add an actual collins check to confirm a machine is a standby
22
+ def is_standby?
23
+ !(running?) || (is_slave? && !taking_connections? && collins_secondary_role == 'standby_slave')
24
+ end
25
+
26
+ # Treat any node outside of current data center as being for backups.
27
+ # This prevents inadvertent cross-data-center master promotion.
28
+ def for_backups?
29
+ hostname.start_with?('backup') || in_remote_datacenter?
30
+ end
31
+
32
+
33
+ ##### CALLBACKS ############################################################
34
+
35
+ # Determine master from Collins if machine is unreachable or MySQL isn't running.
36
+ def after_probe_master
37
+ unless @running
38
+ if collins_secondary_role == 'master'
39
+ @master = false
40
+ else
41
+ pool = Jetpants.topology.pool(collins_pool)
42
+ @master = pool.master if pool
43
+ end
44
+ end
45
+
46
+ # We completely ignore cross-data-center master unless inter_dc_mode is enabled.
47
+ # This may change in a future Jetpants release, once we support tiered replication more cleanly.
48
+ if @master && @master.in_remote_datacenter? && !Jetpants::Plugin::JetCollins.inter_dc_mode?
49
+ @remote_master = @master # keep track of it, in case we need to know later
50
+ @master = false
51
+ elsif !@master
52
+ in_remote_datacenter? # just calling to cache for current node, before we probe its slaves, so that its slaves don't need to query Collins
53
+ end
54
+ end
55
+
56
+ # Determine slaves from Collins if machine is unreachable or MySQL isn't running
57
+ def after_probe_slaves
58
+ # If this machine has a master AND has slaves of its own AND is in another data center,
59
+ # ignore its slaves entirely unless inter_dc_mode is enabled.
60
+ # This may change in a future Jetpants release, once we support tiered replication more cleanly.
61
+ @slaves = [] if @running && @master && @slaves.count > 0 && in_remote_datacenter? && !Jetpants::Plugin::JetCollins.inter_dc_mode?
62
+
63
+ unless @running
64
+ p = Jetpants.topology.pool(self)
65
+ @slaves = (p ? p.slaves_according_to_collins : [])
66
+ end
67
+ end
68
+
69
+
70
+ ##### NEW METHODS ##########################################################
71
+
72
+ def in_remote_datacenter?
73
+ @host.collins_location != Plugin::JetCollins.datacenter
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,41 @@
1
+ # JetCollins monkeypatches to add Collins integration
2
+
3
+ module Jetpants
4
+ class Host
5
+
6
+ ##### JETCOLLINS MIX-IN ####################################################
7
+
8
+ include Plugin::JetCollins
9
+
10
+ def collins_asset
11
+ # try IP first; failing that, try hostname
12
+ selector = {ip_address: ip, details: true}
13
+ selector[:remoteLookup] = true if Jetpants.plugins['jetpants_collins']['remote_lookup']
14
+ assets = Plugin::JetCollins.find selector
15
+
16
+ if (!assets || assets.count == 0) && available?
17
+ selector = {hostname: "^#{hostname}$", details: true}
18
+ selector[:remoteLookup] = true if Jetpants.plugins['jetpants_collins']['remote_lookup']
19
+ assets = Plugin::JetCollins.find selector
20
+ end
21
+
22
+ raise "Multiple assets found for #{self}" if assets.count > 1
23
+ if ! assets || assets.count == 0
24
+ output "WARNING: no Collins assets found for this host"
25
+ nil
26
+ else
27
+ assets.first
28
+ end
29
+ end
30
+
31
+ # Returns which datacenter this host is in. Only a getter, intentionally no setter.
32
+ def collins_location
33
+ return @collins_location if @collins_location
34
+ ca = collins_asset
35
+ @collins_location ||= (ca ? ca.location || Plugin::JetCollins.datacenter : 'unknown')
36
+ @collins_location.upcase!
37
+ @collins_location
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,214 @@
1
+ require 'collins_client'
2
+
3
+ # Entrypoint for jetpants_collins plugin (namespace Jetpants::Plugin::JetCollins),
4
+ # which offers integration with the Collins hardware asset tracking system.
5
+ # This particular file accomplishes the following:
6
+ #
7
+ # * Provides a JetCollins mixin module. Any class including this should also
8
+ # implement a collins_asset method to convert objects to Collins assets;
9
+ # the class can then use the provided collins_get and collins_set wrappers,
10
+ # along with the collins_attr_accessor class method.
11
+ #
12
+ # * Jetpants::Plugin::JetCollins can also be used as a global Collins API
13
+ # client -- the module itself will delegate all missing methods to a
14
+ # Collins::Client object.
15
+ #
16
+ # * Loads monkeypatches for Jetpants classes DB, Host, Pool, Shard, Topology,
17
+ # and Collins class Asset.
18
+ #
19
+ # Configuration options in Jetpants config file include:
20
+ # user => collins account username (required)
21
+ # password => collins account password (required)
22
+ # url => collins URL (required)
23
+ # timeout => collins client timeout, in seconds (default: 30)
24
+ # datacenter => collins data center name that we're running Jetpants in the context of (required if multi-datacenter)
25
+ # remote_lookup => if true, supply remoteLookup parameter to search multiple datacenters (default: false)
26
+
27
+
28
+ module Jetpants
29
+ module Plugin
30
+ module JetCollins
31
+ @collins_service = nil
32
+
33
+ ##### CLASS METHODS ######################################################
34
+
35
+ class << self
36
+
37
+ # We delegate missing class (module) methods to the collins API client,
38
+ # if it responds to them.
39
+ def method_missing(name, *args, &block)
40
+ if service.respond_to? name
41
+ service.send name, *args, &block
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ # Eigenclass mix-in for collins_attr_accessor
48
+ # Calling "collins_attr_accessor :foo" in your class body will create
49
+ # methods collins_foo and collins_foo= which automatically get/set
50
+ # Collins attribute foo
51
+ def included(base)
52
+ base.class_eval do
53
+ def self.collins_attr_accessor(*fields)
54
+ fields.each do |field|
55
+ define_method("collins_#{field}") do
56
+ (collins_get(field) || '').downcase
57
+ end
58
+ define_method("collins_#{field}=") do |value|
59
+ collins_set(field, value)
60
+ end
61
+ end
62
+ end
63
+
64
+ # We make these 4 accessors available to ANY class including this mixin
65
+ collins_attr_accessor :primary_role, :secondary_role, :pool, :status
66
+ end
67
+ end
68
+
69
+ # Returns the 'datacenter' config option for this plugin, or 'UNKNOWN-DC' if
70
+ # none has been configured. This only matters in multi-datacenter Collins
71
+ # topologies.
72
+ def datacenter
73
+ (Jetpants.plugins['jetpants_collins']['datacenter'] || 'UNKNOWN-DC').upcase
74
+ end
75
+
76
+ # Ordinarily, in a multi-dacenter environment, jetpants_collins places a number
77
+ # of restrictions on interacting with assets that aren't in the local datacenter,
78
+ # for safety's sake and to simplify how hierarchical replication trees are represented:
79
+ #
80
+ # * Won't change Collins attributes on remote server node assets.
81
+ # * If a local node has a master in a remote datacenter, it is ignored/hidden.
82
+ # * If a local node has a slave in a remote datacenter, it's treated as a backup_slave,
83
+ # in order to prevent cross-datacenter master promotions. If any of these
84
+ # remote-datacenter slaves have slaves of their own, they're ignored/hidden.
85
+ #
86
+ # You may DISABLE these restrictions by calling enable_inter_dc_mode. Normally you
87
+ # do NOT want to do this, except in special sitautions like a migration between
88
+ # datacenters.
89
+ def enable_inter_dc_mode
90
+ Jetpants.plugins['jetpants_collins']['inter_dc_mode'] = true
91
+ Jetpants.plugins['jetpants_collins']['remote_lookup'] = true
92
+ end
93
+
94
+ # Returns true if enable_inter_dc_mode has been called, false otherwise.
95
+ def inter_dc_mode?
96
+ Jetpants.plugins['jetpants_collins']['inter_dc_mode'] || false
97
+ end
98
+
99
+
100
+ private
101
+
102
+ # Returns a Collins::Client object
103
+ def service
104
+ return @collins_service if @collins_service
105
+
106
+ %w(url user password).each do |setting|
107
+ raise "No Collins #{setting} set in plugins -> jetpants_collins -> #{setting}" unless Jetpants.plugins['jetpants_collins'][setting]
108
+ end
109
+
110
+ logger = Logger.new(STDOUT)
111
+ logger.level = Logger::INFO
112
+ config = {
113
+ :host => Jetpants.plugins['jetpants_collins']['url'],
114
+ :timeout => Jetpants.plugins['jetpants_collins']['timeout'] || 30,
115
+ :username => Jetpants.plugins['jetpants_collins']['user'],
116
+ :password => Jetpants.plugins['jetpants_collins']['password'],
117
+ :logger => logger,
118
+ }
119
+ @collins_service = Collins::Client.new(config)
120
+ end
121
+ end
122
+
123
+
124
+ ##### INSTANCE (MIX-IN) METHODS ##########################################
125
+
126
+ # The base class needs to implement this!
127
+ def collins_asset
128
+ raise "Any class including Plugin::JetCollins must also implement collins_asset instance method!"
129
+ end
130
+
131
+ # Pass in a symbol, or array of symbols, to obtain from Collins for this
132
+ # asset. For example, :status, :pool, :primary_role, :secondary_role.
133
+ # If you pass in a single symbol, returns a single value.
134
+ # If you pass in an array, returns a hash mapping each of these fields to their values.
135
+ # Hash will also contain an extra field called :asset, storing the Collins::Asset object.
136
+ def collins_get(*field_names)
137
+ asset = collins_asset
138
+ if field_names.count > 1 || field_names[0].is_a?(Array)
139
+ field_names.flatten!
140
+ results = Hash[field_names.map {|field| [field, (asset ? asset.send(field) : '')]}]
141
+ results[:asset] = asset
142
+ results
143
+ elsif field_names.count == 1
144
+ return '' unless asset
145
+ asset.send field_names[0]
146
+ else
147
+ nil
148
+ end
149
+ end
150
+
151
+ # Pass in a hash mapping field name symbols to values to set
152
+ # Symbol => String -- optionally set any Collins attribute
153
+ # :status => String -- optionally set the status value for the asset
154
+ # :asset => Collins::Asset -- optionally pass this in to avoid an extra Collins API lookup, if asset already obtained
155
+ #
156
+ # Alternatively, pass in 2 strings (field_name, value) to set just a single Collins attribute (or status)
157
+ def collins_set(*args)
158
+ attrs = (args.count == 1 ? args[0] : {args[0] => args[1]})
159
+ asset = attrs[:asset] || collins_asset
160
+
161
+ # refuse to set Collins values on machines in remote data center unless
162
+ # inter_dc_mode is enabled
163
+ if asset && asset.type.downcase == 'server_node' && asset.location && asset.location.upcase != Plugin::JetCollins.datacenter
164
+ asset = nil unless Jetpants::Plugin::JetCollins.inter_dc_mode?
165
+ end
166
+
167
+ attrs.each do |key, val|
168
+ val ||= ''
169
+ case key
170
+ when :asset
171
+ next
172
+ when :status
173
+ unless asset
174
+ output "WARNING: unable to set Collins status to #{val}"
175
+ next
176
+ end
177
+ previous_value = asset.status
178
+ if previous_value != val.to_s
179
+ success = Jetpants::Plugin::JetCollins.set_status!(asset, val)
180
+ raise "#{self}: Unable to set Collins status to #{val}" unless success
181
+ output "Collins status changed from #{previous_value} to #{val}"
182
+ end
183
+ else
184
+ unless asset
185
+ output "WARNING: unable to set Collins attribute #{key} to #{val}"
186
+ next
187
+ end
188
+ previous_value = asset.send(key)
189
+ if previous_value != val.to_s.upcase
190
+ success = Jetpants::Plugin::JetCollins.set_attribute!(asset, key.to_s.upcase, val.to_s.upcase)
191
+ raise "#{self}: Unable to set Collins attribute #{key} to #{val}" unless success
192
+ if (val.to_s == '' || !val) && (previous_value == '' || !previous_value)
193
+ false
194
+ elsif val.to_s == ''
195
+ output "Collins attribute #{key.to_s.upcase} removed (was: #{previous_value})"
196
+ elsif !previous_value || previous_value == ''
197
+ output "Collins attribute #{key.to_s.upcase} set to #{val.to_s.upcase}"
198
+ else
199
+ output "Collins attribute #{key.to_s.upcase} changed from #{previous_value} to #{val.to_s.upcase}"
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ end
206
+
207
+ end # module JetCollins
208
+ end # module Plugin
209
+ end
210
+
211
+
212
+ # load all the monkeypatches for other Jetpants classes
213
+ %w(asset host db pool shard topology).each {|mod| require "jetpants_collins/#{mod}"}
214
+
@@ -0,0 +1,145 @@
1
+ # JetCollins monkeypatches to add Collins integration
2
+
3
+ module Jetpants
4
+ class Pool
5
+
6
+ ##### JETCOLLINS MIX-IN ####################################################
7
+
8
+ include Plugin::JetCollins
9
+
10
+ # Used at startup time, to keep track of parent/child shard relationships
11
+ attr_accessor :has_parent
12
+
13
+ # Collins accessors for configuration asset metadata
14
+ collins_attr_accessor :slave_pool_name, :aliases, :master_read_weight, :config_sort_order
15
+
16
+ # Returns a Collins::Asset for this pool. Can optionally create one if not found.
17
+ def collins_asset(create_if_missing=false)
18
+ selector = {
19
+ operation: 'and',
20
+ details: true,
21
+ type: 'CONFIGURATION',
22
+ primary_role: 'MYSQL_POOL',
23
+ pool: "^#{@name.upcase}$",
24
+ status: 'Allocated',
25
+ }
26
+ selector[:remoteLookup] = true if Jetpants.plugins['jetpants_collins']['remote_lookup']
27
+
28
+ results = Plugin::JetCollins.find selector
29
+
30
+ # If we got back multiple results, try ignoring the remote datacenter ones
31
+ if results.count > 1
32
+ filtered_results = results.select {|a| a.location.nil? || a.location.upcase == Plugin::JetCollins.datacenter}
33
+ results = filtered_results if filtered_results.count > 0
34
+ end
35
+
36
+ if results.count > 1
37
+ raise "Multiple configuration assets found for pool #{name}"
38
+ elsif results.count == 0 && create_if_missing
39
+ output "Could not find configuration asset for pool; creating now"
40
+ new_tag = 'mysql-' + @name
41
+ asset = Collins::Asset.new type: 'CONFIGURATION', tag: new_tag, status: 'Allocated'
42
+ begin
43
+ Plugin::JetCollins.create!(asset)
44
+ rescue
45
+ collins_set asset: asset,
46
+ status: 'Allocated'
47
+ end
48
+ collins_set asset: asset,
49
+ primary_role: 'MYSQL_POOL',
50
+ pool: @name.upcase
51
+ Plugin::JetCollins.get new_tag
52
+ elsif results.count == 0 && !create_if_missing
53
+ raise "Could not find configuration asset for pool #{name}"
54
+ else
55
+ results.first
56
+ end
57
+ end
58
+
59
+
60
+ ##### METHOD OVERRIDES #####################################################
61
+
62
+ # Examines the current state of the pool (as known to Jetpants) and updates
63
+ # Collins to reflect this, in terms of the pool's configuration asset as
64
+ # well as the individual hosts.
65
+ def sync_configuration
66
+ asset = collins_asset(true)
67
+ collins_set asset: asset,
68
+ slave_pool_name: slave_name || '',
69
+ aliases: aliases.join(',') || '',
70
+ master_read_weight: master_read_weight
71
+ [@master, slaves].flatten.each do |db|
72
+ current_status = (db.collins_status || '').downcase
73
+ db.collins_status = 'Allocated' unless current_status == 'maintenance'
74
+ db.collins_pool = @name
75
+ end
76
+ @master.collins_secondary_role = 'MASTER'
77
+ slaves(:active).each do |db|
78
+ db.collins_secondary_role = 'ACTIVE_SLAVE'
79
+ weight = @active_slave_weights[db]
80
+ db.collins_slave_weight = (weight == 100 ? '' : weight)
81
+ end
82
+
83
+ slaves(:standby).each {|db| db.collins_secondary_role = 'STANDBY_SLAVE'}
84
+ slaves(:backup).each {|db| db.collins_secondary_role = 'BACKUP_SLAVE'}
85
+ true
86
+ end
87
+
88
+ # If the pool's master hasn't been probed yet, return active_slaves list
89
+ # based strictly on what we found in Collins. This is a major speed-up at
90
+ # start-up time, especially for tasks that need to iterate over all pools.
91
+ alias :active_slaves_from_probe :active_slaves
92
+ def active_slaves
93
+ if @master.probed?
94
+ active_slaves_from_probe
95
+ else
96
+ @active_slave_weights.keys
97
+ end
98
+ end
99
+
100
+
101
+ ##### CALLBACKS ############################################################
102
+
103
+ # Pushes slave removal to Collins. (Normally this type of logic is handled by
104
+ # Pool#sync_configuration, but that won't handle this case, since
105
+ # sync_configuration only updates hosts still in the pool.)
106
+ def after_remove_slave!(slave_db)
107
+ slave_db.collins_pool = slave_db.collins_secondary_role = slave_db.collins_slave_weight = ''
108
+ current_status = (slave_db.collins_status || '').downcase
109
+ slave_db.collins_status = 'Unallocated' unless current_status == 'maintenance'
110
+ end
111
+
112
+ # If the demoted master was offline, record some info in Collins, otherwise
113
+ # there will be 2 masters listed
114
+ def after_master_promotion!(promoted)
115
+ Jetpants.topology.clear_asset_cache
116
+
117
+ # Find the master asset(s) for this pool, filtering down to only current datacenter
118
+ assets = Jetpants.topology.server_node_assets(@name, :master)
119
+ assets.reject! {|a| a.location && a.location.upcase != Plugin::JetCollins.datacenter}
120
+ assets.map(&:to_db).each do |db|
121
+ if db != @master || !db.running?
122
+ db.collins_status = 'Maintenance'
123
+ db.collins_pool = ''
124
+ db.collins_secondary_role = ''
125
+ end
126
+ end
127
+ end
128
+
129
+
130
+ ##### NEW METHODS ##########################################################
131
+
132
+ # Called from DB#after_probe_master and DB#after_probe_slave for machines
133
+ # that are unreachable via SSH, or reachable but MySQL isn't running.
134
+ def slaves_according_to_collins
135
+ results = []
136
+ Jetpants.topology.server_node_assets(@name, :slave).each do |asset|
137
+ slave = asset.to_db
138
+ output "Collins found slave #{slave.ip} (#{slave.hostname})"
139
+ results << slave
140
+ end
141
+ results
142
+ end
143
+
144
+ end
145
+ end