repctl 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  *.gem
2
2
  .bundle
3
+ webserver/sass/.sass-cache/*
3
4
  Gemfile.lock
4
5
  pkg/*
data/bin/repctl CHANGED
@@ -5,34 +5,6 @@ require 'fileutils'
5
5
  require 'repctl'
6
6
  require 'thor'
7
7
 
8
- module Repctl
9
- module Helpers
10
- def do_stop(instance)
11
- do_admin(instance, "shutdown")
12
- end
13
-
14
- def do_reset(instance)
15
- do_stop(instance)
16
- do_config(instance)
17
- do_start(instance)
18
- do_secure_accounts(instance)
19
- end
20
-
21
- def do_start_slave(instance)
22
- run_mysql_query(instance, "START SLAVE")
23
- end
24
-
25
- def do_stop_slave(instance)
26
- run_mysql_query(instance, "STOP SLAVE")
27
- end
28
-
29
- def do_restart(instance)
30
- do_admin(instance, "shutown")
31
- do_start(instance)
32
- end
33
- end
34
- end
35
-
36
8
  class RepctlCmds < Thor
37
9
 
38
10
  include Thor::Actions
@@ -40,6 +12,7 @@ class RepctlCmds < Thor
40
12
  include Repctl::Commands
41
13
  include Repctl::Servers
42
14
  include Repctl::Helpers
15
+ include Repctl::Color
43
16
 
44
17
  desc "start [INSTANCES]", "Start one or more defined server instances."
45
18
  method_option :all, :type => :boolean, :aliases => "-a",
@@ -200,49 +173,12 @@ class RepctlCmds < Thor
200
173
  method_option :servers, :aliases => "-s", :type => :array,
201
174
  :desc => "Only check the status of given servers."
202
175
  def status
203
- todos = options[:servers] || all_live_instances
204
- unless todos.any?
205
- say "No Running Servers. Are you running as the mysql user?", :blue
206
- return
207
- end
208
176
  header = sprintf("%-5s%-27s%-27s%-27s%-8s\n",
209
177
  "inst", "master", "received", "applied", "lag")
210
-
211
178
  loop do
212
- say header, :blue
213
-
214
- todos.each do |i|
215
- coordinates = get_coordinates(i)
216
- master_file = coordinates[:file]
217
- master_pos = coordinates[:position]
218
- slave = is_slave?(i)
219
- if slave
220
- slave_status = get_slave_status(i)
221
- recv_file = slave_status["Master_Log_File"]
222
- recv_pos = slave_status["Read_Master_Log_Pos"]
223
- apply_file = slave_status["Relay_Master_Log_File"]
224
- apply_pos = slave_status["Exec_Master_Log_Pos"]
225
- lag = slave_status["Seconds_Behind_Master"]
226
- master_host = slave_status["Master_Host"]
227
- master_port = slave_status["Master_Port"]
228
- master_instance = instance_for(master_host, master_port)
229
- end
230
-
231
- if slave
232
- if lag
233
- lag = lag.to_s
234
- else
235
- lag = "-"
236
- end
237
- format = "%1d%-4s%16s:%-10d%16s:%-10d%16s:%-10d%-8s"
238
- str = sprintf(format, i, "(#{master_instance.to_s})", master_file,
239
- master_pos, recv_file, recv_pos, apply_file, apply_pos, lag)
240
- else
241
- format = "%-5d%16s:%-10d"
242
- str = sprintf(format, i, master_file, master_pos)
243
- end
244
-
245
- say str + "\n", :yellow
179
+ output = formatted_status(options)
180
+ output.each do |line|
181
+ puts line
246
182
  end
247
183
  break unless options[:continuous]
248
184
  sleep options[:continuous]
@@ -255,8 +191,7 @@ class RepctlCmds < Thor
255
191
  coordinates = do_dump(instance, dumpfile)
256
192
  file = coordinates[:file]
257
193
  position = coordinates[:position]
258
- puts "(#{file}, #{position})"
259
- [file, String(position)]
194
+ say "Dumped at coordinates (#{file}, #{position})", :green
260
195
  end
261
196
 
262
197
  desc "restore INSTANCE [DUMPFILE]", "Restore INSTANCE from a \'mysqldump\' file DUMPFILE."
@@ -269,25 +204,14 @@ class RepctlCmds < Thor
269
204
  # master has existing data.
270
205
  #
271
206
  desc "add_slave MASTER SLAVE", "Master has some data that is used to initialize the slave."
272
- method_option :populate, :type => :boolean, :default => false
273
207
  def add_slave(master, slave)
274
- reset(slave)
275
-
276
- if options[:populate]
277
- invoke "utils:bench", [master, "/opt/MySQL/b2.properties"]
278
- end
279
- file, position = invoke "dump", [master]
280
-
281
- # Slave is running, but it is not yet configured as a slave.
282
- # Load slave from the dump file...
283
- invoke "restore", [slave]
284
-
285
- do_change_master(master, slave, :file => file, :position => position)
286
- do_start_slave(slave)
287
-
288
- do_cluster_user(slave)
289
- do_repl_user(slave)
208
+ dumpfile = DEFAULT_DUMPFILE
209
+ do_add_slave(master, slave, dumpfile)
210
+ end
290
211
 
212
+ desc "remove_slave SLAVE", "Remove a slave from the replica set."
213
+ def remove_slave(slave)
214
+ do_remove_slave(slave)
291
215
  end
292
216
 
293
217
  DEFAULT_MASTER = 1
@@ -385,16 +309,7 @@ class RepctlCmds < Thor
385
309
  "Set up a single master/slave replication pair from the very beginning."
386
310
  def repl_pair(master, slave)
387
311
  say "master is #{master}, slave is #{slave}", :green
388
- do_reset(master)
389
- do_reset(slave)
390
- do_cluster_user(master)
391
- do_repl_user(master)
392
- coordinates = get_coordinates(master)
393
- file = coordinates[:file]
394
- position = coordinates[:position]
395
- say "File is #{file}, Position is #{position}", :green
396
- do_change_master(master, slave, :file => file, :position => position)
397
- do_start_slave(slave)
312
+ do_repl_pair(master, slave)
398
313
  end
399
314
 
400
315
  desc "repl_trio MASTER SLAVE1 SLAVE2",
@@ -402,30 +317,7 @@ class RepctlCmds < Thor
402
317
  method_option :reset, :type => :boolean, :default => true
403
318
  def repl_trio(master, slave1, slave2)
404
319
  say "master is #{master}, slaves are #{slave1} and #{slave2}", :green
405
- if options[:reset]
406
- do_reset(master)
407
- do_reset(slave1)
408
- do_reset(slave2)
409
- else
410
- do_restart(master)
411
- do_restart(slave1)
412
- do_restart(slave2)
413
- end
414
- # Set up the replication accounts for all servers, in case we
415
- # decide to switch masters later.
416
- [master, slave1, slave2].each do |s|
417
- do_cluster_user(s)
418
- do_repl_user(s)
419
- end
420
-
421
- coordinates = get_coordinates(master)
422
- file = coordinates[:file]
423
- position = coordinates[:position]
424
- say "File is #{file}, Position is #{position}", :green
425
- do_change_master(master, slave1, :file => file, :position => position)
426
- do_start_slave(slave1)
427
- do_change_master(master, slave2, :file => file, :position => position)
428
- do_start_slave(slave2)
320
+ do_repl_trio(master, slave1, slave2, options)
429
321
  end
430
322
 
431
323
  desc "install_sample_configs DIRECTORY", "After initial install, use these "
data/config/servers.yml CHANGED
@@ -9,6 +9,7 @@
9
9
  innodb_data_home_dir: /opt/MySQL/instances/data1
10
10
  innodb_log_group_home_dir: /opt/MySQL/instances/data1
11
11
  pid-file: /opt/MySQL/instances/data1/deimos.pid
12
+ user: _mysql
12
13
 
13
14
  - instance: 2
14
15
  defaults-file: "/opt/projects/repctl/config/mysql.cnf"
@@ -20,6 +21,7 @@
20
21
  innodb_data_home_dir: /opt/MySQL/instances/data2
21
22
  innodb_log_group_home_dir: /opt/MySQL/instances/data2
22
23
  pid-file: /opt/MySQL/instances/data2/deimos.pid
24
+ user: _mysql
23
25
 
24
26
  - instance: 3
25
27
  defaults-file: "/opt/projects/repctl/config/mysql.cnf"
@@ -31,6 +33,7 @@
31
33
  innodb_data_home_dir: /opt/MySQL/instances/data3
32
34
  innodb_log_group_home_dir: /opt/MySQL/instances/data3
33
35
  pid-file: /opt/MySQL/instances/data3/deimos.pid
36
+ user: _mysql
34
37
 
35
38
  - instance: 4
36
39
  defaults-file: "/opt/projects/repctl/config/mysql.cnf"
@@ -42,4 +45,5 @@
42
45
  innodb_data_home_dir: /opt/MySQL/instances/data4
43
46
  innodb_log_group_home_dir: /opt/MySQL/instances/data4
44
47
  pid-file: /opt/MySQL/instances/data4/deimos.pid
48
+ user: _mysql
45
49
 
@@ -0,0 +1,61 @@
1
+ module Repctl
2
+ module Color
3
+ module Constants
4
+
5
+ # Embed in a String to clear all previous ANSI sequences.
6
+ CLEAR = "\e[0m"
7
+ # The start of an ANSI bold sequence.
8
+ BOLD = "\e[1m"
9
+
10
+ # Set the terminal's foreground ANSI color to black.
11
+ BLACK = "\e[30m"
12
+ # Set the terminal's foreground ANSI color to red.
13
+ RED = "\e[31m"
14
+ # Set the terminal's foreground ANSI color to green.
15
+ GREEN = "\e[32m"
16
+ # Set the terminal's foreground ANSI color to yellow.
17
+ YELLOW = "\e[33m"
18
+ # Set the terminal's foreground ANSI color to blue.
19
+ BLUE = "\e[34m"
20
+ # Set the terminal's foreground ANSI color to magenta.
21
+ MAGENTA = "\e[35m"
22
+ # Set the terminal's foreground ANSI color to cyan.
23
+ CYAN = "\e[36m"
24
+ # Set the terminal's foreground ANSI color to white.
25
+ WHITE = "\e[37m"
26
+
27
+ # Set the terminal's background ANSI color to black.
28
+ ON_BLACK = "\e[40m"
29
+ # Set the terminal's background ANSI color to red.
30
+ ON_RED = "\e[41m"
31
+ # Set the terminal's background ANSI color to green.
32
+ ON_GREEN = "\e[42m"
33
+ # Set the terminal's background ANSI color to yellow.
34
+ ON_YELLOW = "\e[43m"
35
+ # Set the terminal's background ANSI color to blue.
36
+ ON_BLUE = "\e[44m"
37
+ # Set the terminal's background ANSI color to magenta.
38
+ ON_MAGENTA = "\e[45m"
39
+ # Set the terminal's background ANSI color to cyan.
40
+ ON_CYAN = "\e[46m"
41
+ # Set the terminal's background ANSI color to white.
42
+ ON_WHITE = "\e[47m"
43
+ end
44
+
45
+ # Set color by using a string or one of the defined constants. If a third
46
+ # option is set to true, it also adds bold to the string. This is based
47
+ # on Highline implementation and it automatically appends CLEAR to the end
48
+ # of the returned String.
49
+ #
50
+ def colorize(color, bold = false)
51
+ color = Constants.const_get(color.to_s.upcase) if color.is_a?(Symbol)
52
+ bold = bold ? Constants::BOLD : ""
53
+ "#{bold}#{color}#{self.to_s}#{Constants::CLEAR}"
54
+ end
55
+ end
56
+ end
57
+
58
+ String.class_eval do
59
+ include Repctl::Color
60
+ end
61
+
@@ -0,0 +1,146 @@
1
+ module Repctl
2
+ module Helpers
3
+
4
+ include Commands
5
+ include Servers
6
+
7
+ def do_stop(instance)
8
+ do_admin(instance, "shutdown")
9
+ end
10
+
11
+ def do_reset(instance)
12
+ do_stop(instance)
13
+ do_config(instance)
14
+ do_start(instance)
15
+ do_secure_accounts(instance)
16
+ end
17
+
18
+ def do_start_slave(instance)
19
+ run_mysql_query(instance, "START SLAVE")
20
+ end
21
+
22
+ def do_stop_slave(instance)
23
+ run_mysql_query(instance, "STOP SLAVE")
24
+ end
25
+
26
+ def do_restart(instance)
27
+ do_admin(instance, "shutown")
28
+ do_start(instance)
29
+ end
30
+
31
+ # Generate an array of hashes, one hash per fabric-wide instance.
32
+ def repl_status(options = {})
33
+ todos = options[:servers] || all_live_instances
34
+ return [] unless todos.any?
35
+ status_array = []
36
+ todos.each do |i|
37
+ coordinates = get_coordinates(i)
38
+ next unless coordinates
39
+ master_file = coordinates[:file]
40
+ master_pos = coordinates[:position]
41
+
42
+ fields = {}
43
+ fields[:instance] = i.to_s
44
+ fields[:server] = "#{server_for_instance(i)['hostname']}:#{i}"
45
+ fields[:generated_binlog] = "#{master_file}:#{master_pos}"
46
+ if is_slave?(i)
47
+ slave_status = get_slave_status(i)
48
+ recv_file = slave_status["Master_Log_File"]
49
+ recv_pos = slave_status["Read_Master_Log_Pos"]
50
+ apply_file = slave_status["Relay_Master_Log_File"]
51
+ apply_pos = slave_status["Exec_Master_Log_Pos"]
52
+ lag = slave_status["Seconds_Behind_Master"]
53
+ master_host = slave_status["Master_Host"]
54
+ master_port = slave_status["Master_Port"]
55
+ master_instance = instance_for(master_host, master_port)
56
+
57
+ fields[:applied_binlog] = "#{apply_file}:#{apply_pos}"
58
+ fields[:received_binlog] = "#{recv_file}:#{recv_pos}"
59
+ fields[:master] = "#{master_host}:#{master_instance}"
60
+ fields[:lag] = lag
61
+ end
62
+ status_array << fields
63
+ end
64
+ status_array
65
+ end
66
+
67
+ def formatted_status(options = {})
68
+ output = []
69
+ header = sprintf("%-5s%-27s%-27s%-27s%-8s",
70
+ "inst", "master", "received", "applied", "lag")
71
+ output << header.colorize(:green)
72
+ todos = repl_status(options)
73
+ todos.each do |server|
74
+ instance = server[:instance]
75
+ gen_binlog = server[:generated_binlog]
76
+ if server[:master]
77
+ server[:master].match(/.*:(\d*)$/)
78
+ master_instance = $1
79
+ recv_binlog = server[:received_binlog]
80
+ app_binlog = server[:applied_binlog]
81
+ lag = server[:lag]
82
+ if lag == nil
83
+ lag = "-"
84
+ else
85
+ lag = lag.to_s
86
+ end
87
+ format = "%1d%-4s%-27s%-27s%-27s%-8s"
88
+ str = sprintf(format, instance, "(#{master_instance})",
89
+ gen_binlog, recv_binlog, app_binlog, lag)
90
+ else
91
+ format = "%-5d%-26s"
92
+ str = sprintf(format, instance, gen_binlog)
93
+ end
94
+ output << str.colorize(:yellow)
95
+ end
96
+ output
97
+ end
98
+
99
+ def do_add_slave(master, slave, dumpfile)
100
+ do_reset(slave)
101
+ coordinates = do_dump(master, dumpfile)
102
+ do_restore(slave, dumpfile)
103
+ do_change_master(master, slave, coordinates)
104
+ do_start_slave(slave)
105
+ do_cluster_user(slave)
106
+ do_repl_user(slave)
107
+ end
108
+
109
+ def do_repl_pair(master, slave)
110
+ do_reset(master)
111
+ do_reset(slave)
112
+ do_cluster_user(master)
113
+ do_repl_user(master)
114
+ coordinates = get_coordinates(master)
115
+ file = coordinates[:file]
116
+ position = coordinates[:position]
117
+ do_change_master(master, slave, :file => file, :position => position)
118
+ do_start_slave(slave)
119
+ end
120
+
121
+ def do_repl_trio(master, slave1, slave2, options = {})
122
+ if options[:reset]
123
+ do_reset(master)
124
+ do_reset(slave1)
125
+ do_reset(slave2)
126
+ else
127
+ do_restart(master)
128
+ do_restart(slave1)
129
+ do_restart(slave2)
130
+ end
131
+ # Set up the replication accounts for all servers, in case we
132
+ # decide to switch masters later.
133
+ [master, slave1, slave2].each do |s|
134
+ do_cluster_user(s)
135
+ do_repl_user(s)
136
+ end
137
+ coordinates = get_coordinates(master)
138
+ file = coordinates[:file]
139
+ position = coordinates[:position]
140
+ do_change_master(master, slave1, :file => file, :position => position)
141
+ do_start_slave(slave1)
142
+ do_change_master(master, slave2, :file => file, :position => position)
143
+ do_start_slave(slave2)
144
+ end
145
+ end
146
+ end
@@ -7,7 +7,6 @@ module Repctl
7
7
 
8
8
  class Client < DelegateClass(Mysql2::Client)
9
9
  include Servers
10
-
11
10
  @@clients = {}
12
11
 
13
12
  def initialize(instance, opts)
@@ -36,7 +35,6 @@ module Repctl
36
35
  while timeout >= 0
37
36
  begin
38
37
  @@clients[instance] ||= Client.new(instance, opts)
39
- # puts "Connected to instance #{instance}."
40
38
  break
41
39
  rescue Mysql2::Error => e
42
40
  puts "#{e.message}, retrying connection to instance #{instance}..."
@@ -59,7 +57,6 @@ module Repctl
59
57
  @@clients[@instance] = nil
60
58
  Client.open(@instance)
61
59
  end
62
-
63
60
  end
64
61
 
65
62
  module Commands
@@ -131,7 +128,7 @@ module Repctl
131
128
  "--innodb_log_group_home_dir=#{server['innodb_log_group_home_dir']}",
132
129
  "--relay-log=#{Socket.gethostname}-relay-bin",
133
130
  "--socket=#{server['socket']}",
134
- "--user=mysql")
131
+ "--user=#{server['user']}")
135
132
  end
136
133
  end
137
134
  end
@@ -415,14 +412,6 @@ EOT
415
412
  client.close if client
416
413
  end
417
414
 
418
- # Return the process ID (pid) for an instance.
419
- def get_mysqld_pid(instance)
420
- server = server_for_instance(instance)
421
- pidfile = server['pid-file']
422
- return nil unless File.exist?(pidfile)
423
- Integer(File.open(pidfile, &:readline).strip)
424
- end
425
-
426
415
  # 'master' is currently a slave that is to be the new master.
427
416
  # 'slaves' contains the list of slaves, one of these may be the
428
417
  # current master.
@@ -448,6 +437,13 @@ EOT
448
437
  end
449
438
  end
450
439
 
440
+ def do_remove_slave(instance)
441
+ stop_slave_io_thread(instance)
442
+ server = server_for_instance(instance)
443
+ datadir = server['datadir']
444
+ %x{ rm -f #{File.join(datadir, 'master.info')} }
445
+ end
446
+
451
447
  private
452
448
 
453
449
  # This is an example template to create commands to issue queries.
@@ -596,30 +592,9 @@ EOT
596
592
  client.close
597
593
  end
598
594
  end
599
-
600
-
601
-
602
595
  end
603
596
  end
604
597
 
605
- class RunExamples
606
- include Repctl::Commands
607
-
608
- def runtest
609
-
610
- switch_master_to(3)
611
- =begin
612
- (1..4).each {|i| ensure_running(i)}
613
- puts get_master_coordinates(1)
614
- (2..4).each {|i| puts get_slave_coordinates(i)}
615
- (1..4).each {|i| puts is_master?(i) }
616
- puts "Master is #{find_master}"
617
- drain_relay_log(2)
618
- # sleep(10)
619
- # (1..4).each {|i| crash(i)}
620
- =end
621
- end
622
- end
623
598
 
624
599
 
625
600
 
@@ -23,7 +23,13 @@ module Repctl
23
23
  end
24
24
 
25
25
  def all_live_servers
26
- all_servers.select {|s| get_mysqld_pid(s["instance"]) }
26
+ s = all_servers.select do |s|
27
+ if pid = get_mysqld_pid(s["instance"])
28
+ mysqld_running?(pid)
29
+ else
30
+ false
31
+ end
32
+ end
27
33
  end
28
34
 
29
35
  def all_live_instances
@@ -43,9 +49,13 @@ module Repctl
43
49
  return nil
44
50
  end
45
51
 
46
- private
52
+ # See if a MySQL server with the given pid is running.
53
+ def mysqld_running?(pid)
54
+ pids = %x{ ps -e | grep mysqld}.split("\n").map { |row| row =~ /\s*(\d+)\s+/; $1.to_i}
55
+ pids.include?(pid)
56
+ end
47
57
 
48
- # Return the process ID (pid) for an instance.
58
+ # Return the process ID (pid) for an instance. This only consults the PID file.
49
59
  def get_mysqld_pid(instance)
50
60
  server = server_for_instance(instance)
51
61
  pidfile = server['pid-file']
@@ -1,3 +1,3 @@
1
1
  module Repctl
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
data/lib/repctl.rb CHANGED
@@ -4,9 +4,11 @@ config_dir = ENV["REPCTL_CONFIG_DIR"] ||
4
4
 
5
5
  require File.join(config_dir, 'config')
6
6
 
7
- require "repctl/version"
8
- require "repctl/servers"
9
- require "repctl/mysql_admin"
7
+ require 'repctl/version'
8
+ require 'repctl/servers'
9
+ require 'repctl/mysql_admin'
10
+ require 'repctl/helpers'
11
+ require 'repctl/color'
10
12
 
11
13
  module Repctl
12
14
  # Your code goes here...
data/repctl.gemspec CHANGED
@@ -20,6 +20,11 @@ Gem::Specification.new do |s|
20
20
 
21
21
  # specify any dependencies here; for example:
22
22
  s.add_development_dependency "rspec"
23
+ s.add_development_dependency "shotgun"
24
+ s.add_development_dependency "compass"
23
25
  s.add_runtime_dependency "thor"
24
26
  s.add_runtime_dependency "mysql2"
27
+ s.add_runtime_dependency "sinatra"
28
+ s.add_runtime_dependency "thin"
29
+
25
30
  end
Binary file
@@ -0,0 +1,24 @@
1
+ # Require any additional compass plugins here.
2
+
3
+ # Set this to the root of your project when deployed:
4
+ http_path = "/"
5
+ css_dir = "stylesheets"
6
+ sass_dir = "sass"
7
+ images_dir = "images"
8
+ javascripts_dir = "javascripts"
9
+
10
+ # You can select your preferred output style here (can be overridden via the command line):
11
+ # output_style = :expanded or :nested or :compact or :compressed
12
+
13
+ # To enable relative paths to assets via compass helper functions. Uncomment:
14
+ # relative_assets = true
15
+
16
+ # To disable debugging comments that display the original location of your selectors. Uncomment:
17
+ # line_comments = false
18
+
19
+
20
+ # If you prefer the indented syntax, you might want to regenerate this
21
+ # project again passing --syntax sass, or you can uncomment this:
22
+ # preferred_syntax = :sass
23
+ # and then run:
24
+ # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
@@ -0,0 +1,5 @@
1
+ /* Welcome to Compass. Use this file to write IE specific override styles.
2
+ * Import this file using the following HTML or equivalent:
3
+ * <!--[if IE]>
4
+ * <link href="/stylesheets/ie.css" media="screen, projection" rel="stylesheet" type="text/css" />
5
+ * <![endif]--> */
@@ -0,0 +1,3 @@
1
+ /* Welcome to Compass. Use this file to define print styles.
2
+ * Import this file using the following HTML or equivalent:
3
+ * <link href="/stylesheets/print.css" media="print" rel="stylesheet" type="text/css" /> */
@@ -0,0 +1,6 @@
1
+ /* Welcome to Compass.
2
+ * In this file you should write your main styles. (or centralize your imports)
3
+ * Import this file using the following HTML or equivalent:
4
+ * <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css" /> */
5
+
6
+ @import "compass/reset";
@@ -0,0 +1,5 @@
1
+ /* Welcome to Compass. Use this file to write IE specific override styles.
2
+ * Import this file using the following HTML or equivalent:
3
+ * <!--[if IE]>
4
+ * <link href="/stylesheets/ie.css" media="screen, projection" rel="stylesheet" type="text/css" />
5
+ * <![endif]--> */
@@ -0,0 +1,3 @@
1
+ /* Welcome to Compass. Use this file to define print styles.
2
+ * Import this file using the following HTML or equivalent:
3
+ * <link href="/stylesheets/print.css" media="print" rel="stylesheet" type="text/css" /> */
@@ -0,0 +1,68 @@
1
+ /* Welcome to Compass.
2
+ * In this file you should write your main styles. (or centralize your imports)
3
+ * Import this file using the following HTML or equivalent:
4
+ * <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css" /> */
5
+ /* line 17, ../../../../../../usr/local/rvm/gems/ruby-1.9.2-p290@rails-3.1/gems/compass-0.11.7/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
6
+ html, body, div, span, applet, object, iframe,
7
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8
+ a, abbr, acronym, address, big, cite, code,
9
+ del, dfn, em, img, ins, kbd, q, s, samp,
10
+ small, strike, strong, sub, sup, tt, var,
11
+ b, u, i, center,
12
+ dl, dt, dd, ol, ul, li,
13
+ fieldset, form, label, legend,
14
+ table, caption, tbody, tfoot, thead, tr, th, td,
15
+ article, aside, canvas, details, embed,
16
+ figure, figcaption, footer, header, hgroup,
17
+ menu, nav, output, ruby, section, summary,
18
+ time, mark, audio, video {
19
+ margin: 0;
20
+ padding: 0;
21
+ border: 0;
22
+ font-size: 100%;
23
+ font: inherit;
24
+ vertical-align: baseline;
25
+ }
26
+
27
+ /* line 20, ../../../../../../usr/local/rvm/gems/ruby-1.9.2-p290@rails-3.1/gems/compass-0.11.7/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
28
+ body {
29
+ line-height: 1;
30
+ }
31
+
32
+ /* line 22, ../../../../../../usr/local/rvm/gems/ruby-1.9.2-p290@rails-3.1/gems/compass-0.11.7/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
33
+ ol, ul {
34
+ list-style: none;
35
+ }
36
+
37
+ /* line 24, ../../../../../../usr/local/rvm/gems/ruby-1.9.2-p290@rails-3.1/gems/compass-0.11.7/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
38
+ table {
39
+ border-collapse: collapse;
40
+ border-spacing: 0;
41
+ }
42
+
43
+ /* line 26, ../../../../../../usr/local/rvm/gems/ruby-1.9.2-p290@rails-3.1/gems/compass-0.11.7/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
44
+ caption, th, td {
45
+ text-align: left;
46
+ font-weight: normal;
47
+ vertical-align: middle;
48
+ }
49
+
50
+ /* line 28, ../../../../../../usr/local/rvm/gems/ruby-1.9.2-p290@rails-3.1/gems/compass-0.11.7/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
51
+ q, blockquote {
52
+ quotes: none;
53
+ }
54
+ /* line 101, ../../../../../../usr/local/rvm/gems/ruby-1.9.2-p290@rails-3.1/gems/compass-0.11.7/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
55
+ q:before, q:after, blockquote:before, blockquote:after {
56
+ content: "";
57
+ content: none;
58
+ }
59
+
60
+ /* line 30, ../../../../../../usr/local/rvm/gems/ruby-1.9.2-p290@rails-3.1/gems/compass-0.11.7/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
61
+ a img {
62
+ border: none;
63
+ }
64
+
65
+ /* line 114, ../../../../../../usr/local/rvm/gems/ruby-1.9.2-p290@rails-3.1/gems/compass-0.11.7/frameworks/compass/stylesheets/compass/reset/_utilities.scss */
66
+ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary {
67
+ display: block;
68
+ }
@@ -0,0 +1,80 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>MySQL Replication Manager</title>
6
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
7
+ </script>
8
+
9
+ <script type="text/javascript">
10
+ /* Fix this. It should not be in the global namespace. */
11
+ function getUpdate() {
12
+ $('#status-table').load('/status');
13
+ setTimeout(getUpdate, 3000);
14
+ }
15
+ $(document).ready(function() {
16
+ getUpdate();
17
+
18
+ $('#switch-master').submit(function() {
19
+ $('.spinner').show();
20
+ $.post('/switch_master', $(this).serialize(), function(data) {
21
+ $('.spinner').hide();
22
+ $("#switch-master-result").html(data)
23
+ });
24
+ return false;
25
+ });
26
+
27
+ $('#repl-trio').submit(function() {
28
+ $('#repl-trio-spinner').show();
29
+ $.post('/repl_trio', $(this).serialize(), function(data) {
30
+ $('#repl-trio-spinner').hide();
31
+ $("#repl-trio-result").html(data)
32
+ });
33
+ return false;
34
+ });
35
+
36
+ });
37
+ </script>
38
+
39
+ <style type="text/css">
40
+ #banner {
41
+ text-align: center;
42
+ }
43
+ .spinner {
44
+ display:none;
45
+ }
46
+ .success {
47
+ color: green;
48
+ }
49
+ .failure {
50
+ color: red;
51
+ }
52
+ table.gridtable {
53
+ font-family: verdana,arial,sans-serif;
54
+ font-size:11px;
55
+ color:#333333;
56
+ border-width: 1px;
57
+ border-color: #666666;
58
+ border-collapse: collapse;
59
+ }
60
+ table.gridtable th {
61
+ border-width: 1px;
62
+ padding: 8px;
63
+ border-style: solid;
64
+ border-color: #666666;
65
+ background-color: #dedede;
66
+ }
67
+ table.gridtable td {
68
+ border-width: 1px;
69
+ padding: 8px;
70
+ border-style: solid;
71
+ border-color: #666666;
72
+ background-color: #ffffff;
73
+ }
74
+ </style>
75
+ </head>
76
+
77
+ <body>
78
+ <%= yield %>
79
+ </body>
80
+ </html>
@@ -0,0 +1,38 @@
1
+ <header id="banner">
2
+ <h1>MySQL Replication Manager</h1>
3
+ </header>
4
+
5
+ <section>
6
+ <header>
7
+ <h2>Status Summary</h2>
8
+ </header>
9
+ <div id="status-table">
10
+ </div>
11
+ </section>
12
+
13
+ <section>
14
+ <header><h2>Switch Master</h2></header>
15
+ <form id="switch-master" action="/switch_master" method="post">
16
+ <p>Master: <input type="text" name="switch[master]" size="20"/></p>
17
+ <p>Slaves: <input type="text" name="switch[slaves]" size="20"/></p>
18
+ <input type="submit" value="Switch Master">
19
+ </form>
20
+ <div>
21
+ <div style="float:left;" id="switch-master-result"></div>
22
+ <img style="float:left;" class='spinner' id="switch-master-spinner" src='images/wait30.gif' alt='spinner'></img>
23
+ </div>
24
+ </section>
25
+
26
+ <section>
27
+ <header><h2>Set up Replication Trio from Scratch</h2></header>
28
+ <form id="repl-trio" action="/repl_trio" method="post">
29
+ <p>Master: <input type="text" name="repl_trio[master]" size="20"/></p>
30
+ <p>Slaves: <input type="text" name="repl_trio[slaves]" size="20"/></p>
31
+ <input type="submit" value="Create Repliction Trio">
32
+ </form>
33
+ <div>
34
+ <div style="float:left;" id="repl-trio-result"></div>
35
+ <img style="float:left;" class="spinner" id='repl-trio-spinner' src='images/wait30.gif' alt='spinner'></img>
36
+ </div>
37
+ </section>
38
+
@@ -0,0 +1,5 @@
1
+ <% if @success %>
2
+ <span class="success"><%= @message %></span>
3
+ <% else %>
4
+ <span class="failure"><%= @message %></span>
5
+ <% end %>
@@ -0,0 +1,24 @@
1
+ <div>
2
+ Status as of <%= @timestamp %>
3
+ </div>
4
+ <table class="gridtable">
5
+ <tr>
6
+ <th>instance</th>
7
+ <th>master</th>
8
+ <th>generated binlog</th>
9
+ <th>received binlog</th>
10
+ <th>applied binlog</th>
11
+ <th>lag (secs)</th>
12
+ </tr>
13
+ <% @status_array.each do |s| %>
14
+ <tr>
15
+ <td><%= s[:server] %></td>
16
+ <td><%= s[:master] %></td>
17
+ <td><%= s[:generated_binlog] %></td>
18
+ <td><%= s[:received_binlog] %></td>
19
+ <td><%= s[:applied_binlog] %></td>
20
+ <td><%= s[:lag] %></td>
21
+ </tr>
22
+ <% end %>
23
+ </table>
24
+
@@ -0,0 +1,108 @@
1
+ require 'sinatra'
2
+ require 'repctl'
3
+
4
+ include Repctl::Config
5
+ include Repctl::Commands
6
+ include Repctl::Servers
7
+ include Repctl::Utils
8
+ include Repctl::Color
9
+
10
+ def time
11
+ start = Time.now
12
+ yield
13
+ Time.now - start
14
+ end
15
+
16
+ helpers do
17
+ def img(name)
18
+ "<img src='images/#{ name}' alt='#{ name}' />"
19
+ end
20
+
21
+ def master_slave_params(params)
22
+ param_error = false
23
+ @error_message = nil
24
+ if params.nil? || params["master"] == "" || params["master"].nil? ||
25
+ params["slaves"] == nil || params["slaves"] == ""
26
+ @error_message = "parameters missing"
27
+ else
28
+ begin
29
+ master = params["master"].to_i
30
+ slaves = params["slaves"].split("\s").map(&:to_i)
31
+ rescue Exception => e
32
+ @error_message = "invalid format for parameters"
33
+ else
34
+ if master == 0 || slaves.include?(0)
35
+ @error_message = "0 can not be an instance"
36
+ elsif slaves.include?(master)
37
+ @error_message = "master can not also be a slave"
38
+ elsif slaves.empty?
39
+ @error_message = "no slaves are specified"
40
+ end
41
+ end
42
+ end
43
+ [@error_message, master, slaves]
44
+ end
45
+ end
46
+
47
+ get '/' do
48
+ erb :main
49
+ end
50
+
51
+ # curl deimos:9393/switch_master --header "Accept: text/plain" \
52
+ # -d switch[slaves]="2 1 4" -d switch[master]=3
53
+ post '/switch_master' do
54
+ @message, master, slaves = master_slave_params(params["switch"])
55
+ if @message
56
+ @success = false
57
+ else
58
+ secs = time do
59
+ do_switch_master(master, slaves)
60
+ end
61
+ @message = "Switch master processed in #{secs} secs."
62
+ @success = true
63
+ end
64
+ if request.accept == ['text/plain']
65
+ if @success
66
+ "#{@message}\n".colorize(:green)
67
+ else
68
+ "#{@message}\n".colorize(:red)
69
+ end
70
+ else
71
+ erb :operation_complete, :layout => !request.xhr?
72
+ end
73
+ end
74
+
75
+ post '/repl_trio' do
76
+ @message, master, slaves = master_slave_params(params["repl_trio"])
77
+ if @message
78
+ @success = false
79
+ else
80
+ secs = time do
81
+ sleep 1
82
+ end
83
+ @message = "Create replication trio with master #{master} and slaves #{slaves} in #{secs} secs."
84
+ @success = true
85
+ end
86
+ if request.accept == ['text/plain']
87
+ if @success
88
+ "#{@message}\n".colorize(:green)
89
+ else
90
+ "#{@message}\n".colorize(:red)
91
+ end
92
+ else
93
+ erb :operation_complete, :layout => !request.xhr?
94
+ end
95
+ end
96
+
97
+ # curl deimos:9393/status --header "Accept: text/plain"
98
+ get '/status' do
99
+ if request.accept == ['text/plain']
100
+ formatted_status.join("\n") + "\n"
101
+ else
102
+ @timestamp = Time.now.strftime("%I:%M:%S %p")
103
+ @status_array = repl_status
104
+ erb :status , :layout => !request.xhr?
105
+ end
106
+ end
107
+
108
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: repctl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-17 00:00:00.000000000Z
12
+ date: 2012-03-08 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &76697300 !ruby/object:Gem::Requirement
16
+ requirement: &121456320 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,32 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *76697300
24
+ version_requirements: *121456320
25
+ - !ruby/object:Gem::Dependency
26
+ name: shotgun
27
+ requirement: &121455900 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *121455900
36
+ - !ruby/object:Gem::Dependency
37
+ name: compass
38
+ requirement: &121455480 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *121455480
25
47
  - !ruby/object:Gem::Dependency
26
48
  name: thor
27
- requirement: &76696880 !ruby/object:Gem::Requirement
49
+ requirement: &121455060 !ruby/object:Gem::Requirement
28
50
  none: false
29
51
  requirements:
30
52
  - - ! '>='
@@ -32,10 +54,32 @@ dependencies:
32
54
  version: '0'
33
55
  type: :runtime
34
56
  prerelease: false
35
- version_requirements: *76696880
57
+ version_requirements: *121455060
36
58
  - !ruby/object:Gem::Dependency
37
59
  name: mysql2
38
- requirement: &76696460 !ruby/object:Gem::Requirement
60
+ requirement: &121454640 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *121454640
69
+ - !ruby/object:Gem::Dependency
70
+ name: sinatra
71
+ requirement: &121454220 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *121454220
80
+ - !ruby/object:Gem::Dependency
81
+ name: thin
82
+ requirement: &121453800 !ruby/object:Gem::Requirement
39
83
  none: false
40
84
  requirements:
41
85
  - - ! '>='
@@ -43,7 +87,7 @@ dependencies:
43
87
  version: '0'
44
88
  type: :runtime
45
89
  prerelease: false
46
- version_requirements: *76696460
90
+ version_requirements: *121453800
47
91
  description: Ruby gem with Thor script to manage MySQL and PostgreSQL replication
48
92
  email:
49
93
  - lydianblues@gmail.com
@@ -63,10 +107,25 @@ files:
63
107
  - config/mysql.cnf
64
108
  - config/servers.yml
65
109
  - lib/repctl.rb
110
+ - lib/repctl/color.rb
111
+ - lib/repctl/helpers.rb
66
112
  - lib/repctl/mysql_admin.rb
67
113
  - lib/repctl/servers.rb
68
114
  - lib/repctl/version.rb
69
115
  - repctl.gemspec
116
+ - webserver/public/images/wait30.gif
117
+ - webserver/sass/config.rb
118
+ - webserver/sass/sass/ie.scss
119
+ - webserver/sass/sass/print.scss
120
+ - webserver/sass/sass/screen.scss
121
+ - webserver/sass/stylesheets/ie.css
122
+ - webserver/sass/stylesheets/print.css
123
+ - webserver/sass/stylesheets/screen.css
124
+ - webserver/views/layout.erb
125
+ - webserver/views/main.erb
126
+ - webserver/views/operation_complete.erb
127
+ - webserver/views/status.erb
128
+ - webserver/webserver.rb
70
129
  homepage: ''
71
130
  licenses: []
72
131
  post_install_message: