flare-tools 0.1.4 → 0.4.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/Flare-tools.txt +0 -0
- data/History.txt +114 -2
- data/LICENSE +21 -0
- data/Manifest.txt +65 -8
- data/README.txt +356 -0
- data/Rakefile +90 -25
- data/Tutorial.txt +370 -0
- data/bin/flare-admin +6 -0
- data/bin/flare-argv0 +6 -0
- data/bin/flare-deploy +6 -0
- data/bin/flare-keychecker +6 -0
- data/bin/flare-part +6 -0
- data/bin/flare-ping +6 -0
- data/bin/flare-stats +4 -10
- data/bin/flare-zkadmin +6 -0
- data/lib/flare/net/connection.rb +98 -0
- data/lib/flare/test/cluster.rb +140 -0
- data/lib/flare/test/daemon.rb +144 -0
- data/lib/flare/test/node.rb +62 -0
- data/lib/flare/tools.rb +18 -16
- data/lib/flare/tools/cli.rb +32 -0
- data/lib/flare/tools/cli/activate.rb +106 -0
- data/lib/flare/tools/cli/balance.rb +83 -0
- data/lib/flare/tools/cli/cli_util.rb +77 -0
- data/lib/flare/tools/cli/deploy.rb +170 -0
- data/lib/flare/tools/cli/down.rb +85 -0
- data/lib/flare/tools/cli/dump.rb +219 -0
- data/lib/flare/tools/cli/dumpkey.rb +117 -0
- data/lib/flare/tools/cli/flare_admin.rb +81 -0
- data/lib/flare/tools/cli/flare_argv0.rb +60 -0
- data/lib/flare/tools/cli/flare_keychecker.rb +106 -0
- data/lib/flare/tools/cli/flare_zkadmin.rb +226 -0
- data/lib/flare/tools/cli/index.rb +54 -0
- data/lib/flare/tools/cli/list.rb +93 -0
- data/lib/flare/tools/cli/master.rb +143 -0
- data/lib/flare/tools/cli/part.rb +100 -0
- data/lib/flare/tools/cli/ping.rb +81 -0
- data/lib/flare/tools/cli/reconstruct.rb +164 -0
- data/lib/flare/tools/cli/remove.rb +119 -0
- data/lib/flare/tools/cli/restore.rb +180 -0
- data/lib/flare/tools/cli/slave.rb +125 -0
- data/lib/flare/tools/cli/stats.rb +229 -122
- data/lib/flare/tools/cli/sub_command.rb +73 -0
- data/lib/flare/tools/cli/summary.rb +97 -0
- data/lib/flare/tools/cli/threads.rb +78 -0
- data/lib/flare/tools/cli/verify.rb +202 -0
- data/lib/flare/tools/client.rb +267 -0
- data/lib/flare/tools/cluster.rb +319 -0
- data/lib/flare/tools/common.rb +196 -0
- data/lib/flare/tools/index_server.rb +51 -0
- data/lib/flare/tools/node.rb +162 -0
- data/lib/flare/tools/stats.rb +75 -0
- data/lib/flare/tools/zk_util.rb +28 -0
- data/lib/flare/util.rb +34 -0
- data/lib/flare/util/bwlimit.rb +132 -0
- data/lib/flare/util/command_line.rb +79 -0
- data/lib/flare/util/conf.rb +71 -0
- data/lib/flare/util/constant.rb +25 -0
- data/lib/flare/util/conversion.rb +26 -0
- data/lib/flare/util/default_logger.rb +52 -0
- data/lib/flare/util/exception.rb +19 -0
- data/lib/flare/util/filesystem.rb +30 -0
- data/lib/flare/util/flared_conf.rb +33 -0
- data/lib/flare/util/flarei_conf.rb +32 -0
- data/lib/flare/util/hash_function.rb +32 -0
- data/lib/flare/util/interruption.rb +70 -0
- data/lib/flare/util/key_resolver.rb +67 -0
- data/lib/flare/util/log4r_logger.rb +79 -0
- data/lib/flare/util/logger.rb +40 -0
- data/lib/flare/util/logging.rb +84 -0
- data/lib/flare/util/result.rb +53 -0
- data/test/test/experimental/cache_test.rb +113 -0
- data/test/test/experimental/key_distribution_test.rb +38 -0
- data/test/test/experimental/keychecker_test.rb +60 -0
- data/test/test/experimental/list_test.rb +108 -0
- data/test/test/extra/replication_test.rb +184 -0
- data/test/test/integration/cli_test.rb +348 -0
- data/test/test/integration/dump_expired_test.rb +103 -0
- data/test/test/integration/dump_test.rb +128 -0
- data/test/test/integration/index_server_test.rb +35 -0
- data/test/test/integration/node_test.rb +78 -0
- data/test/test/integration/partition_test.rb +235 -0
- data/test/test/integration/proxy_test.rb +54 -0
- data/test/test/integration/stats_test.rb +79 -0
- data/test/test/system/flare_admin_test.rb +191 -0
- data/test/test/unit/bwlimit_test.rb +52 -0
- data/test/test/unit/cluster_test.rb +96 -0
- data/test/test/unit/daemon_test.rb +30 -0
- data/test/test/unit/logger_test.rb +46 -0
- data/test/test/unit/tools_test.rb +25 -0
- data/test/test/unit/util_test.rb +70 -0
- metadata +239 -84
- data/README.rdoc +0 -83
- data/bin/flare-partition-setting +0 -12
- data/lib/flare/tools/cli/partition_setting.rb +0 -86
- data/lib/flare/tools/core.rb +0 -189
- data/lib/flare/tools/logger.rb +0 -31
- data/test/test_flare-tools.rb +0 -11
- data/test/test_helper.rb +0 -3
@@ -0,0 +1,93 @@
|
|
1
|
+
# -*- coding: utf-8; -*-
|
2
|
+
# Authors:: Kiyoshi Ikehara <kiyoshi.ikehara@gree.net>
|
3
|
+
# Copyright:: Copyright (C) GREE, Inc. 2011.
|
4
|
+
# License:: MIT-style
|
5
|
+
|
6
|
+
require 'flare/tools/index_server'
|
7
|
+
require 'flare/tools/cluster'
|
8
|
+
require 'flare/tools/common'
|
9
|
+
require 'flare/util/conversion'
|
10
|
+
require 'flare/tools/cli/sub_command'
|
11
|
+
|
12
|
+
#
|
13
|
+
module Flare
|
14
|
+
module Tools
|
15
|
+
module Cli
|
16
|
+
|
17
|
+
# == Description
|
18
|
+
#
|
19
|
+
class List < SubCommand
|
20
|
+
include Flare::Util::Conversion
|
21
|
+
include Flare::Tools::Common
|
22
|
+
|
23
|
+
myname :list
|
24
|
+
desc "show the list of nodes in a flare cluster."
|
25
|
+
usage "list"
|
26
|
+
|
27
|
+
HeaderConfig = [ ['%-32s', 'node'],
|
28
|
+
['%9s', 'partition'],
|
29
|
+
['%6s', 'role'],
|
30
|
+
['%6s', 'state'],
|
31
|
+
['%7s', 'balance'] ]
|
32
|
+
|
33
|
+
def setup(opt)
|
34
|
+
opt.on('--numeric-hosts', "show numerical host addresses") {@numeric_hosts = true}
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
super
|
39
|
+
@numeric_hosts = false
|
40
|
+
@format = HeaderConfig.map {|x| x[0]}.join(' ')
|
41
|
+
@cout = STDOUT
|
42
|
+
end
|
43
|
+
|
44
|
+
def print_header
|
45
|
+
@cout.puts @format % HeaderConfig.map{|x| x[1]}.flatten
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def print_node *args
|
50
|
+
@cout.puts @format % args
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_address_or_remain hostname
|
55
|
+
begin
|
56
|
+
Resolv.getaddress(hostname)
|
57
|
+
rescue Resolv::ResolvError
|
58
|
+
hostname
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def execute(config, *args)
|
63
|
+
if args.size > 0
|
64
|
+
error "invalid arguments: "+args.join(' ')
|
65
|
+
return S_NG
|
66
|
+
end
|
67
|
+
|
68
|
+
cluster = Flare::Tools::IndexServer.open(config[:index_server_hostname],
|
69
|
+
config[:index_server_port], config[:timeout]) do |s|
|
70
|
+
Flare::Tools::Cluster.new(s.host, s.port, s.stats_nodes)
|
71
|
+
end
|
72
|
+
|
73
|
+
if cluster.nil?
|
74
|
+
error "Invalid index server."
|
75
|
+
return S_NG
|
76
|
+
end
|
77
|
+
|
78
|
+
print_header
|
79
|
+
cluster.nodekeys.each do |nodekey|
|
80
|
+
data = cluster.node_stat(nodekey)
|
81
|
+
hostname, port = nodekey.split(":", 2)
|
82
|
+
hostname = get_address_or_remain(hostname) if @numeric_hosts
|
83
|
+
partition = (data.partition == -1) ? "-" : data.partition
|
84
|
+
print_node nodekey_of(hostname, port), partition, data.role, data.state, data.balance
|
85
|
+
end
|
86
|
+
|
87
|
+
S_OK
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# -*- coding: utf-8; -*-
|
2
|
+
# Authors:: Kiyoshi Ikehara <kiyoshi.ikehara@gree.net>
|
3
|
+
# Copyright:: Copyright (C) GREE, Inc. 2011.
|
4
|
+
# License:: MIT-style
|
5
|
+
|
6
|
+
require 'flare/tools/stats'
|
7
|
+
require 'flare/tools/index_server'
|
8
|
+
require 'flare/tools/common'
|
9
|
+
require 'flare/tools/cluster'
|
10
|
+
require 'flare/util/conversion'
|
11
|
+
require 'flare/util/constant'
|
12
|
+
require 'flare/tools/cli/sub_command'
|
13
|
+
|
14
|
+
module Flare
|
15
|
+
module Tools
|
16
|
+
module Cli
|
17
|
+
class Master < SubCommand
|
18
|
+
include Flare::Util::Conversion
|
19
|
+
include Flare::Util::Constant
|
20
|
+
include Flare::Tools::Common
|
21
|
+
|
22
|
+
myname :master
|
23
|
+
desc "construct a partition with a proxy node for master role."
|
24
|
+
usage "master [hostname:port:balance:partition] ..."
|
25
|
+
|
26
|
+
def setup(opt)
|
27
|
+
opt.on('--force', "commit changes without confirmation" ) {@force = true}
|
28
|
+
opt.on('--retry=COUNT', "specify retry count (default:#{@retry})" ) {|v| @retry = v.to_i}
|
29
|
+
opt.on('--activate', "change node's state from ready to active") {@activate = true}
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@force = false
|
34
|
+
@retry = 10
|
35
|
+
@activate = false
|
36
|
+
end
|
37
|
+
|
38
|
+
def execute(config, *args)
|
39
|
+
status = S_OK
|
40
|
+
|
41
|
+
return S_NG if args.empty?
|
42
|
+
hosts = args.map {|x| x.to_s.split(':')}
|
43
|
+
hosts.each do |x|
|
44
|
+
if x.size != 4
|
45
|
+
error "invalid argument '#{x.join(':')}'."
|
46
|
+
return S_NG
|
47
|
+
end
|
48
|
+
if x[2].to_i <= 0
|
49
|
+
error "invalid balance '#{x.join(':')}'."
|
50
|
+
return S_NG
|
51
|
+
end
|
52
|
+
if nodekey_of(x[0..1]).nil?
|
53
|
+
error "invalid nodekey '#{x.join(':')}'."
|
54
|
+
return S_NG
|
55
|
+
end
|
56
|
+
end
|
57
|
+
hosts = hosts.sort_by{|hostname,port,balance,partition| [partition]}
|
58
|
+
|
59
|
+
Flare::Tools::IndexServer.open(config[:index_server_hostname], config[:index_server_port], config[:timeout]) do |s|
|
60
|
+
cluster = Flare::Tools::Cluster.new(s.host, s.port, s.stats_nodes)
|
61
|
+
|
62
|
+
hosts.each do |hostname,port,balance,partition|
|
63
|
+
role = 'master'
|
64
|
+
nodekey = nodekey_of hostname, port
|
65
|
+
ipaddr = address_of_hostname(hostname)
|
66
|
+
|
67
|
+
unless cluster.has_nodekey? nodekey
|
68
|
+
error "unknown host: #{nodekey}"
|
69
|
+
# return S_NG
|
70
|
+
end
|
71
|
+
|
72
|
+
node = cluster.node_stat(nodekey)
|
73
|
+
|
74
|
+
partition = if partition == '' then node['partition'].to_i else partition.to_i end
|
75
|
+
balance = if balance == '' then node['balance'] else balance.to_i end
|
76
|
+
existing_master = cluster.master_in_partition(partition)
|
77
|
+
|
78
|
+
exec = false
|
79
|
+
if @force
|
80
|
+
exec = true
|
81
|
+
elsif node['role'] == role
|
82
|
+
info "no need to change the role of #{ipaddr}:#{port}."
|
83
|
+
elsif existing_master
|
84
|
+
info "the partiton already has a master #{existing_master}."
|
85
|
+
else
|
86
|
+
STDERR.print "making the node master (node=#{ipaddr}:#{port}, role=#{node['role']} -> #{role}) (y/n): "
|
87
|
+
exec = interruptible {(gets.chomp.upcase == "Y")}
|
88
|
+
end
|
89
|
+
if exec && !config[:dry_run]
|
90
|
+
nretry = 0
|
91
|
+
resp = false
|
92
|
+
while resp == false && nretry < @retry
|
93
|
+
resp = s.set_role(hostname, port, role, balance, partition)
|
94
|
+
if resp
|
95
|
+
info "started constructing the master node..."
|
96
|
+
else
|
97
|
+
nretry += 1
|
98
|
+
info "waiting #{nretry} sec..."
|
99
|
+
sleep nretry
|
100
|
+
info "retrying..."
|
101
|
+
end
|
102
|
+
end
|
103
|
+
if resp
|
104
|
+
state = wait_for_master_construction(s, nodekey, config[:timeout])
|
105
|
+
if state == 'ready' && @activate
|
106
|
+
unless @force
|
107
|
+
node = s.stats_nodes[nodekey]
|
108
|
+
STDERR.print "changing node's state (node=#{ipaddr}:#{port}, state=#{node['state']} -> active) (y/n): "
|
109
|
+
exec = interruptible {
|
110
|
+
(gets.chomp.upcase == "Y")
|
111
|
+
}
|
112
|
+
end
|
113
|
+
if exec
|
114
|
+
begin
|
115
|
+
resp = s.set_state(hostname, port, 'active')
|
116
|
+
unless resp
|
117
|
+
error "failed to activate #{nodekey}"
|
118
|
+
status = S_NG
|
119
|
+
end
|
120
|
+
rescue Timeout::Error
|
121
|
+
error "failed to activate #{nodekey} (timeout)"
|
122
|
+
status = S_NG
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
else
|
127
|
+
error "failed to change the state."
|
128
|
+
status = S_NG
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
break if status == S_NG
|
134
|
+
STDOUT.puts string_of_nodelist(s.stats_nodes, hosts.map {|x| "#{x[0]}:#{x[1]}"})
|
135
|
+
end
|
136
|
+
|
137
|
+
status
|
138
|
+
end # execute()
|
139
|
+
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# -*- coding: utf-8; -*-
|
2
|
+
# Authors:: Kiyoshi Ikehara <kiyoshi.ikehara@gree.net>
|
3
|
+
# Copyright:: Copyright (C) GREE, Inc. 2011.
|
4
|
+
# License:: MIT-style
|
5
|
+
|
6
|
+
require 'flare/tools/stats'
|
7
|
+
require 'flare/tools/index_server'
|
8
|
+
require 'flare/tools/common'
|
9
|
+
require 'flare/util/conversion'
|
10
|
+
require 'flare/util/constant'
|
11
|
+
require 'flare/tools/cli/sub_command'
|
12
|
+
require 'flare/tools/cli/slave'
|
13
|
+
require 'flare/tools/cli/master'
|
14
|
+
|
15
|
+
module Flare
|
16
|
+
module Tools
|
17
|
+
module Cli
|
18
|
+
class Part < SubCommand
|
19
|
+
include Flare::Util::Conversion
|
20
|
+
include Flare::Util::Constant
|
21
|
+
include Flare::Tools::Common
|
22
|
+
|
23
|
+
myname :part
|
24
|
+
desc "set the master of a partition."
|
25
|
+
usage "master [hostname:port:balance:partition] ..."
|
26
|
+
|
27
|
+
def setup(opt)
|
28
|
+
opt.on('--force', "commits changes without confirmation") {@force = true}
|
29
|
+
opt.on('--retry=COUNT', "retry count" ) {|v| @retry = v.to_i}
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
super
|
34
|
+
@force = false
|
35
|
+
@retry = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def execute(config, *args)
|
39
|
+
return S_NG if args.size < 1
|
40
|
+
|
41
|
+
hosts = args.map {|x| x.to_s.split(':')}
|
42
|
+
hosts.each do |x|
|
43
|
+
if x.size != 4
|
44
|
+
puts "invalid argument '#{x.join(':')}'."
|
45
|
+
return S_NG
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
masters = []
|
50
|
+
slaves = []
|
51
|
+
|
52
|
+
Flare::Tools::IndexServer.open(config[:index_server_hostname], config[:index_server_port], config[:timeout]) do |s|
|
53
|
+
cluster = Flare::Tools::Cluster.new(s.host, s.port, s.stats_nodes)
|
54
|
+
|
55
|
+
partitions = {}
|
56
|
+
hosts.each do |hostname,port,balance,partition|
|
57
|
+
partitions[partition] = [] unless partitions.has_key? partition
|
58
|
+
partitions[partition] << "#{hostname}:#{port}:#{balance}:#{partition}"
|
59
|
+
end
|
60
|
+
|
61
|
+
partitions.sort_by {|p,nodes| p.to_i }.each do |p,nodes|
|
62
|
+
masters << nodes.shift
|
63
|
+
end
|
64
|
+
|
65
|
+
partitions.each do |p,nodes|
|
66
|
+
slaves.concat nodes
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
puts "master:"
|
71
|
+
begin
|
72
|
+
opt = OptionParser.new
|
73
|
+
subc = Flare::Tools::Cli::Master.new
|
74
|
+
subc.setup(opt)
|
75
|
+
args = masters
|
76
|
+
args << "--force" if @force
|
77
|
+
args << "--activate"
|
78
|
+
opt.parse!(args)
|
79
|
+
subc.execute(config, *args)
|
80
|
+
end
|
81
|
+
|
82
|
+
puts "slaves:"
|
83
|
+
begin
|
84
|
+
opt = OptionParser.new
|
85
|
+
subc = Flare::Tools::Cli::Slave.new
|
86
|
+
subc.setup(opt)
|
87
|
+
args = slaves
|
88
|
+
args << "--force" if @force
|
89
|
+
args << "--retry=#{@retry}" unless @retry.nil?
|
90
|
+
opt.parse!(args)
|
91
|
+
subc.execute(config, *args)
|
92
|
+
end
|
93
|
+
|
94
|
+
S_OK
|
95
|
+
end # execute()
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# -*- coding: utf-8; -*-
|
2
|
+
# Authors:: Kiyoshi Ikehara <kiyoshi.ikehara@gree.net>
|
3
|
+
# Copyright:: Copyright (C) GREE, Inc. 2011.
|
4
|
+
# License:: MIT-style
|
5
|
+
|
6
|
+
require 'flare/tools/index_server'
|
7
|
+
require 'flare/util/conversion'
|
8
|
+
require 'flare/util/logging'
|
9
|
+
require 'flare/tools/cli/sub_command'
|
10
|
+
|
11
|
+
#
|
12
|
+
module Flare
|
13
|
+
module Tools
|
14
|
+
|
15
|
+
# == Description
|
16
|
+
#
|
17
|
+
module Cli
|
18
|
+
class Ping < SubCommand
|
19
|
+
include Flare::Util::Conversion
|
20
|
+
include Flare::Util::Logging
|
21
|
+
|
22
|
+
myname :ping
|
23
|
+
desc "ping"
|
24
|
+
usage "ping [hostname:port] ..."
|
25
|
+
|
26
|
+
def setup(opt)
|
27
|
+
opt.on('--wait', "wait for OK responses from nodes") {@wait = true}
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
super
|
32
|
+
@wait = false
|
33
|
+
end
|
34
|
+
|
35
|
+
def execute(config, *args)
|
36
|
+
|
37
|
+
hosts = args.map do |arg|
|
38
|
+
hostname, port, rest = arg.split(':', 3)
|
39
|
+
if !rest.nil? || hostname.nil? || hostname.empty? || port.nil? || port.empty?
|
40
|
+
error "invalid argument '#{arg}'. it must be hostname:port."
|
41
|
+
return S_NG
|
42
|
+
end
|
43
|
+
begin
|
44
|
+
ipaddr = Resolv.getaddress(hostname)
|
45
|
+
rescue Resolv::ResolvError
|
46
|
+
error "unknown host '#{hostname}'"
|
47
|
+
return S_NG
|
48
|
+
end
|
49
|
+
[hostname, port]
|
50
|
+
end
|
51
|
+
|
52
|
+
hosts.each do |hostname, port|
|
53
|
+
resp = nil
|
54
|
+
until resp
|
55
|
+
begin
|
56
|
+
debug "trying..."
|
57
|
+
interruptible do
|
58
|
+
Flare::Tools::Stats.open(hostname, port, config[:timeout]) do |s|
|
59
|
+
resp = s.ping
|
60
|
+
end
|
61
|
+
end
|
62
|
+
rescue IOError
|
63
|
+
return S_NG
|
64
|
+
rescue
|
65
|
+
unless @wait
|
66
|
+
puts "#{hostname}:#{port} is down"
|
67
|
+
return S_NG
|
68
|
+
end
|
69
|
+
interruptible {sleep 1}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
puts "alive"
|
75
|
+
S_OK
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# -*- coding: utf-8; -*-
|
2
|
+
# Authors:: Kiyoshi Ikehara <kiyoshi.ikehara@gree.net>
|
3
|
+
# Copyright:: Copyright (C) GREE, Inc. 2011.
|
4
|
+
# License:: MIT-style
|
5
|
+
|
6
|
+
require 'flare/tools/stats'
|
7
|
+
require 'flare/tools/node'
|
8
|
+
require 'flare/tools/index_server'
|
9
|
+
require 'flare/tools/cluster'
|
10
|
+
require 'flare/tools/common'
|
11
|
+
require 'flare/util/conversion'
|
12
|
+
require 'flare/util/constant'
|
13
|
+
require 'flare/tools/cli/sub_command'
|
14
|
+
|
15
|
+
module Flare
|
16
|
+
module Tools
|
17
|
+
module Cli
|
18
|
+
class Reconstruct < SubCommand
|
19
|
+
include Flare::Util::Conversion
|
20
|
+
include Flare::Util::Constant
|
21
|
+
include Flare::Tools::Common
|
22
|
+
|
23
|
+
myname :reconstruct
|
24
|
+
desc "reconstruct the database of nodes by copying."
|
25
|
+
usage "reconstruct [hostname:port] ..."
|
26
|
+
|
27
|
+
def setup(opt)
|
28
|
+
opt.on('--force', "commit changes without confirmation" ) {@force = true}
|
29
|
+
opt.on('--safe', "reconstruct a node safely" ) {@safe = true}
|
30
|
+
opt.on('--retry=COUNT', "specify retry count (default:#{@retry})") {|v| @retry = v.to_i}
|
31
|
+
opt.on('--all', "reconstruct all nodes" ) {@all = true}
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
super
|
36
|
+
@force = false
|
37
|
+
@safe = false
|
38
|
+
@retry = 10
|
39
|
+
@all = false
|
40
|
+
end
|
41
|
+
|
42
|
+
def execute(config, *args)
|
43
|
+
if @all
|
44
|
+
unless args.empty?
|
45
|
+
puts "don't specify any nodes with --all option."
|
46
|
+
return S_NG
|
47
|
+
else
|
48
|
+
Flare::Tools::IndexServer.open(config[:index_server_hostname], config[:index_server_port], config[:timeout]) do |s|
|
49
|
+
cluster = Flare::Tools::Cluster.new(s.host, s.port, s.stats_nodes)
|
50
|
+
args = cluster.master_and_slave_nodekeys
|
51
|
+
end
|
52
|
+
end
|
53
|
+
else
|
54
|
+
return S_NG if args.size == 0
|
55
|
+
end
|
56
|
+
|
57
|
+
hosts = args.map {|x| x.to_s.split(':')}
|
58
|
+
hosts.each do |x|
|
59
|
+
if x.size != 2
|
60
|
+
puts "invalid argument '#{x.join(':')}'. it must be hostname:port."
|
61
|
+
return S_NG
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
status = S_OK
|
66
|
+
|
67
|
+
Flare::Tools::IndexServer.open(config[:index_server_hostname], config[:index_server_port], config[:timeout]) do |s|
|
68
|
+
puts string_of_nodelist(s.stats_nodes, hosts.map {|x| nodekey_of(x[0], x[1])})
|
69
|
+
|
70
|
+
hosts.each do |hostname,port|
|
71
|
+
nodekey = nodekey_of hostname, port
|
72
|
+
cluster = Flare::Tools::Cluster.new(s.host, s.port, s.stats_nodes)
|
73
|
+
|
74
|
+
unless node = cluster.node_stat(nodekey)
|
75
|
+
puts "#{nodekey} is not found in this cluster."
|
76
|
+
return S_NG
|
77
|
+
end
|
78
|
+
unless cluster.reconstructable? nodekey
|
79
|
+
puts "#{nodekey} is not reconstructable."
|
80
|
+
status = S_NG
|
81
|
+
next
|
82
|
+
end
|
83
|
+
is_safe = cluster.safely_reconstructable? nodekey
|
84
|
+
if @safe && !is_safe
|
85
|
+
puts "The partition needs one more slave to reconstruct #{nodekey} safely."
|
86
|
+
status = S_NG
|
87
|
+
next
|
88
|
+
end
|
89
|
+
|
90
|
+
exec = @force
|
91
|
+
unless exec
|
92
|
+
puts "you are trying to reconstruct #{nodekey} without redanduncy." unless is_safe
|
93
|
+
input = nil
|
94
|
+
while input.nil?
|
95
|
+
STDERR.print "reconstructing node (node=#{nodekey}, role=#{node['role']}) (y/n/a/q/h:help): "
|
96
|
+
input = interruptible do
|
97
|
+
gets.chomp.upcase
|
98
|
+
end
|
99
|
+
case input
|
100
|
+
when "A"
|
101
|
+
@force = true
|
102
|
+
exec = true
|
103
|
+
when "N"
|
104
|
+
when "Q"
|
105
|
+
return S_OK
|
106
|
+
when "Y"
|
107
|
+
exec = true
|
108
|
+
else
|
109
|
+
puts "y: execute, n: skip, a: execute all the left nodes, q: quit, h: help"
|
110
|
+
input = nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
if exec && !config[:dry_run]
|
115
|
+
puts "turning down..."
|
116
|
+
s.set_state(hostname, port, 'down')
|
117
|
+
|
118
|
+
puts "waiting for node to be active again..."
|
119
|
+
sleep 3
|
120
|
+
|
121
|
+
Flare::Tools::Node.open(hostname, port, config[:timeout]) do |n|
|
122
|
+
n.flush_all
|
123
|
+
end
|
124
|
+
|
125
|
+
nretry = 0
|
126
|
+
resp = false
|
127
|
+
while resp == false && nretry < @retry
|
128
|
+
resp = s.set_role(hostname, port, 'slave', 0, node['partition'])
|
129
|
+
if resp
|
130
|
+
puts "started constructing node..."
|
131
|
+
else
|
132
|
+
nretry += 1
|
133
|
+
puts "waiting #{nretry} sec..."
|
134
|
+
sleep nretry
|
135
|
+
puts "retrying..."
|
136
|
+
end
|
137
|
+
end
|
138
|
+
balance = node['balance']
|
139
|
+
if resp
|
140
|
+
wait_for_slave_construction(s, nodekey, config[:timeout])
|
141
|
+
unless @force
|
142
|
+
print "changing node's balance (node=#{nodekey}, balance=0 -> #{balance}) (y/n): "
|
143
|
+
exec = interruptible {(gets.chomp.upcase == "Y")}
|
144
|
+
end
|
145
|
+
s.set_role(hostname, port, 'slave', node['balance'], node['partition']) if exec
|
146
|
+
puts "done."
|
147
|
+
else
|
148
|
+
error "failed to change the state."
|
149
|
+
status = S_NG
|
150
|
+
end
|
151
|
+
end
|
152
|
+
@force = false if interrupted?
|
153
|
+
end
|
154
|
+
|
155
|
+
puts string_of_nodelist(s.stats_nodes, hosts.map {|x| "#{x[0]}:#{x[1]}"})
|
156
|
+
end # open
|
157
|
+
|
158
|
+
status
|
159
|
+
end # execute()
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|