mysqlknife 1.4.0 → 2.0.0

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.
@@ -0,0 +1,123 @@
1
+ # encoding: utf-8
2
+
3
+ module Mysqlknife
4
+ class Configs
5
+ include Singleton
6
+
7
+ attr_writer :mysql_host,
8
+ :mysql_databases
9
+ attr_reader :path,
10
+ :ssh_host,
11
+ :ssh_port,
12
+ :ssh_user,
13
+ :ssh_password,
14
+ :ssh_key,
15
+ :ssh_color,
16
+ :ssh_use,
17
+ :connections,
18
+ :configs,
19
+ :name,
20
+ :mysql_host,
21
+ :mysql_username,
22
+ :mysql_password,
23
+ :mysql_port,
24
+ :mysql_databases
25
+
26
+ def initialize
27
+ # Define path for config file:
28
+ if ENV['ENV'] == 'test'
29
+ path = File.expand_path(File.join(Dir.pwd, '.db.yml'))
30
+ else
31
+ path = File.expand_path("#{Dir.home}/.db.yml")
32
+ end
33
+
34
+ # Load config file:
35
+ @configs = YAML.load_file(path) if File.exist?(path)
36
+
37
+ if @configs.nil?
38
+ puts "Not exist config file in #{path}."
39
+ exit 1
40
+ end
41
+
42
+ # Define generic variables:
43
+ @path = path
44
+ @ssh_host = @configs['ssh']['host']
45
+ @ssh_port = @configs['ssh']['port']
46
+ @ssh_user = @configs['ssh']['user']
47
+ @ssh_password = @configs['ssh']['password']
48
+ @ssh_key = @configs['ssh']['keys']
49
+ @connections = @configs['databases'].keys.sort
50
+ end
51
+
52
+ def method_missing(name, *args)
53
+ method = name.to_s.split(/_/)
54
+ if method.first == 'db'
55
+ param = method[1]
56
+ conn = args[0]
57
+
58
+ if @configs['databases'].include?(conn)
59
+ @configs['databases'][conn][param]
60
+ else
61
+ puts "Not exist connecion name: #{conn}"
62
+ exit 1
63
+ end
64
+ end
65
+ end
66
+
67
+ def settings(name)
68
+ @name = name
69
+ @ssh_color = db_color(name)
70
+ @ssh_use = db_ssh(name)
71
+ @mysql_port = db_port(name)
72
+ @mysql_username = db_username(name)
73
+ @mysql_password = db_password(name)
74
+ end
75
+
76
+ def slaves(name)
77
+ slaves = db_slaves(name)
78
+
79
+ if slaves.nil?
80
+ []
81
+ else
82
+ slaves
83
+ end
84
+ end
85
+
86
+ def hosts(name)
87
+ hosts = []
88
+ hosts << db_host(name)
89
+ hosts.concat(slaves(name))
90
+ hosts.sort
91
+ end
92
+
93
+ def show(name)
94
+ host = db_host(name)
95
+ port = db_port(name)
96
+ username = db_username(name)
97
+ password = db_password(name)
98
+ slaves = db_slaves(name)
99
+ slaves = slaves.join(', ') unless slaves.nil?
100
+
101
+ %W[Master:\ #{host}
102
+ Slaves:\ #{slaves}
103
+ Port:\ #{port}
104
+ Username:\ #{username}
105
+ Password:\ #{password}].join("\n")
106
+ end
107
+
108
+ def tools(name)
109
+ unless db_tools(name).nil?
110
+ db_tools(name).map {|tool| tool.split.first(1).first }
111
+ else
112
+ puts "Please, add custom tools in config file."
113
+ exit 1
114
+ end
115
+ end
116
+
117
+ def tool(name, tool)
118
+ unless db_tools(name).nil?
119
+ db_tools(name).select { |command| /#{tool}/.match(command.to_s) }.first
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ module Mysqlknife
4
+ class Iterm
5
+ @@colors = { orange: [255,128,000],
6
+ green: [000,255,000],
7
+ red: [255,000,000] }
8
+
9
+ def name(name = nil)
10
+ print "\033]0;#{name}\007"
11
+ end
12
+
13
+ def color(name)
14
+ name = name.to_sym
15
+
16
+ red = @@colors[name][0]
17
+ green = @@colors[name][1]
18
+ blue = @@colors[name][2]
19
+
20
+ print "\033]6;1;bg;red;brightness;#{red}\a"
21
+ print "\033]6;1;bg;green;brightness;#{green}\a"
22
+ print "\033]6;1;bg;blue;brightness;#{blue}\a"
23
+ end
24
+
25
+ def reset
26
+ print "\033]6;1;bg;*;default\a"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ module Mysqlknife
4
+ class Log
5
+ attr_writer :verbose
6
+
7
+ def initialize
8
+ @log = Logger.new(STDOUT)
9
+ end
10
+
11
+ def kill(process)
12
+ process = process.map { |k, v| "#{k}: #{v}" }.join(', ')
13
+ @log.info("Kill MySQL Process: #{process}")
14
+ end
15
+
16
+ def sql(statement)
17
+ @log.debug("SQL Statement: #{statement}") if $DEBUG
18
+ end
19
+
20
+ def sql_error(error, sql)
21
+ @log.debug("SQL Error: #{error}")
22
+ @log.debug("SQL Statement: #{sql}")
23
+ end
24
+
25
+ def command(command)
26
+ @log.debug("Bash Command: #{command}") if $DEBUG
27
+ end
28
+
29
+ def skip_repl_error(status)
30
+ status = status.map { |k, v| "#{k}: #{v}" }.join(', ')
31
+ @log.info("MySQL Skip error replica: #{status}")
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ module Mysqlknife
4
+ class MySQL
5
+ def initialize
6
+ @cnf = Configs.instance
7
+ end
8
+
9
+ def execute(sql)
10
+ conn = Mysql2::Client.new(host: @cnf.mysql_host,
11
+ port: @cnf.mysql_port,
12
+ username: @cnf.mysql_username,
13
+ password: @cnf.mysql_password,
14
+ database: @cnf.mysql_database)
15
+
16
+ Mysqlknife::Log.new.sql(sql)
17
+
18
+ return conn.query(sql)
19
+ rescue Mysql2::Error => error
20
+ Mysqlknife::Log.new.sql_error(error, sql)
21
+ exit(1)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+
3
+ module Mysqlknife
4
+ module Mysql
5
+ class Command
6
+ def initialize
7
+ @cnf = Configs.instance
8
+ end
9
+
10
+ def check
11
+ find_executable('mysql')
12
+ find_executable('mysqlshow')
13
+ end
14
+
15
+ def parse(command)
16
+ host = @cnf.mysql_host
17
+ port = @cnf.mysql_port
18
+ username = @cnf.mysql_username
19
+ password = password(@cnf.mysql_password)
20
+
21
+ eval("\"#{command}\"")
22
+ end
23
+
24
+ def prompt
25
+ if !!(@cnf.mysql_host =~ Resolv::IPv4::Regex)
26
+ @cnf.name
27
+ else
28
+ @cnf.mysql_host.partition('.').first
29
+ end
30
+ end
31
+
32
+ def execute(sentence)
33
+ "--execute='#{sentence}'" unless sentence.nil?
34
+ end
35
+
36
+ def password(password)
37
+ "-p#{password}" unless password.nil?
38
+ end
39
+
40
+ def console(sentence = nil)
41
+ %W[mysql
42
+ -h #{@cnf.mysql_host}
43
+ -P #{@cnf.mysql_port}
44
+ -u #{@cnf.mysql_username}
45
+ #{password(@cnf.mysql_password)}
46
+ --prompt='#{prompt}[\\d]> '
47
+ #{@cnf.mysql_database}
48
+ #{execute(sentence)}
49
+ ].join(' ')
50
+ end
51
+
52
+ def describe(*args)
53
+ %W[mysqlshow
54
+ -h #{@cnf.mysql_host}
55
+ -P #{@cnf.mysql_port}
56
+ -u #{@cnf.mysql_username}
57
+ #{password(@cnf.mysql_password)}
58
+ --keys
59
+ #{args.join(' ')}].join(' ')
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+
3
+ module Mysqlknife
4
+ module Mysql
5
+ class Kill
6
+ attr_writer :where
7
+
8
+ def initialize
9
+ @mysql = MySQL.new
10
+ @mysql_sql = Mysql::SQL.new
11
+ end
12
+
13
+ def clear
14
+ list.each do |process|
15
+ kill(process['id'])
16
+
17
+ Mysqlknife::Log.new.kill(process)
18
+ end
19
+ end
20
+
21
+ def check_user
22
+ sql = @mysql_sql.current_user
23
+ result = @mysql.execute(sql).first['CURRENT_USER'] =~ /^root\@/
24
+
25
+ return true if result
26
+ end
27
+
28
+ def check_grants
29
+ sql = @mysql_sql.current_user_grants
30
+ result = @mysql.execute(sql).first.values.to_s =~ /PROCESS/
31
+
32
+ return true if result
33
+ end
34
+
35
+ def check_privileges
36
+ check_user || check_grants
37
+ end
38
+
39
+ def list
40
+ @mysql.execute(@mysql_sql.show_processlist(@where))
41
+ end
42
+
43
+ def show
44
+ process = list
45
+
46
+ unless process.first.nil?
47
+ table = Terminal::Table.new do |t|
48
+ t.add_row(process.first.keys)
49
+ t.add_separator
50
+ process.each do |row|
51
+ t.add_row(row.values)
52
+ end
53
+ end
54
+ puts table
55
+ end
56
+ end
57
+
58
+ def rds?
59
+ ! @mysql.execute(@mysql_sql.show_procedure('rds_kill')).first
60
+ end
61
+
62
+ def kill(id)
63
+ if check_privileges
64
+ if rds?
65
+ @mysql.execute(@mysql_sql.mysql_kill(id))
66
+ else
67
+ @mysql.execute(@mysql_sql.rds_kill(id))
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ module Mysqlknife
4
+ module Mysql
5
+ class Replica
6
+ attr_writer :behind
7
+
8
+ def initialize
9
+ @mysql = MySQL.new
10
+ @mysql_cmd = Mysql::Command.new
11
+ @mysql_sql = Mysql::SQL.new
12
+ end
13
+
14
+ def check_status_negative(behind)
15
+ @behind < 0 && behind < 0 && behind <= @behind && behind != 0
16
+ end
17
+
18
+ def rds?
19
+ ! @mysql.execute(@mysql_sql.show_procedure('rds_skip_repl_error')).first
20
+ end
21
+
22
+ def slave_status
23
+ @mysql.execute(@mysql_sql.slave_status).first
24
+ end
25
+
26
+ def skip
27
+ status = slave_status
28
+
29
+ if check_status_negative(status['Seconds_Behind_Master'])
30
+ Mysqlknife::Log.new.skip_repl_error(status)
31
+
32
+ if rds?
33
+ @mysql.execute(@mysql_sql.rds_skip_repl_error)
34
+ else
35
+ @mysql.execute(@mysql_sql.mysql_skip_repl_error)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ module Mysqlknife
4
+ module Mysql
5
+ class SQL
6
+ def current_user
7
+ 'SELECT CURRENT_USER;'
8
+ end
9
+
10
+ def current_user_grants
11
+ 'SHOW GRANTS FOR CURRENT_USER;'
12
+ end
13
+
14
+ def replica_lag
15
+ 'SHOW SLAVE STATUS\G'
16
+ end
17
+
18
+ def show_processlist(where = nil)
19
+ sql = %w(SELECT id, user, host, db, command, time, state, info
20
+ FROM INFORMATION_SCHEMA.PROCESSLIST
21
+ WHERE state NOT REGEXP '(slave|relay|event)'
22
+ AND user NOT IN ('rdsadmin',
23
+ 'rdsrepladmin',
24
+ 'system user',
25
+ 'event_scheduler')
26
+ AND id != CONNECTION_ID()
27
+ AND command != 'Binlog Dump').join(' ')
28
+ sql << " AND #{where};" unless where.nil?
29
+ end
30
+
31
+ def show_procedure(name)
32
+ "SHOW PROCEDURE STATUS LIKE '#{name}';"
33
+ end
34
+
35
+ def mysql_skip_repl_error
36
+ %w(STOP SLAVE;
37
+ SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
38
+ START SLAVE;).join(' ')
39
+ end
40
+
41
+ def mysql_kill(pid)
42
+ "kill #{pid};"
43
+ end
44
+
45
+ def slave_status
46
+ 'SHOW SLAVE STATUS;'
47
+ end
48
+
49
+ def rds_kill(pid)
50
+ "CALL mysql.rds_kill(#{pid});"
51
+ end
52
+
53
+ def rds_skip_repl_error
54
+ 'CALL mysql.rds_skip_repl_error;'
55
+ end
56
+ end
57
+ end
58
+ end