filecluster 0.4.5 → 0.4.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +1 -0
- data/bin/fc-daemon +1 -0
- data/bin/fc-manage +10 -2
- data/bin/fc-setup-db +35 -15
- data/lib/daemon/copy_task_thread.rb +14 -2
- data/lib/fc/copy_rule.rb +2 -6
- data/lib/fc/db.rb +86 -39
- data/lib/fc/item.rb +26 -5
- data/lib/fc/policy.rb +11 -27
- data/lib/fc/storage.rb +24 -9
- data/lib/fc/var.rb +23 -0
- data/lib/fc/version.rb +1 -1
- data/lib/manage.rb +1 -0
- data/lib/manage/copy_speed.rb +74 -0
- data/lib/manage/storages.rb +13 -1
- data/lib/utils.rb +10 -3
- data/test/copy_rule_test.rb +10 -1
- data/test/helper.rb +1 -1
- data/test/item_test.rb +34 -4
- data/test/policy_test.rb +12 -0
- data/test/storage_test.rb +2 -0
- metadata +21 -40
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 65712f8a51ce15000b05922338f8621f5cd4caec
|
4
|
+
data.tar.gz: 4802352703fc5964a40517f8c7d95eb2a87c0892
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 57a574d9d3ab392e09861425b267fda89c7485e9b107253db65edf77d58809067eb60e1d1b1072cdfd7cd1befc2578ab83eb65063cee9121553a129c78cb9340
|
7
|
+
data.tar.gz: 94dc33d6b766cfd6958ae38f0e1760ef5557dd81fd63ed314687ce08e3c925cbe4f7aea0d6483a4750b3a2cb4c89a5413c01482ea00878e8db9b7b9ecbf2b7d3
|
data/README.md
CHANGED
@@ -59,6 +59,7 @@ Can be used the following variables:
|
|
59
59
|
|daemon_tasks_copy_threads_limit|10|copy tasks threads count limit for one storage|
|
60
60
|
|daemon_tasks_delete_threads_limit|10|delete tasks threads count limit for one storage|
|
61
61
|
|daemon_copy_tasks_per_host_limit|10|copy tasks count limit for one host|
|
62
|
+
|daemon_copy_speed_per_host_limit|0|copy tasks speed limit for hosts, change via fc-manage copy_speed|
|
62
63
|
|daemon_global_tasks_group_limit|1000|select limit for create copy tasks|
|
63
64
|
|daemon_global_error_items_ttl|86400|ttl for items with error status before delete|
|
64
65
|
|daemon_global_error_items_storages_ttl|86400|ttl for items_storages with error status before delete|
|
data/bin/fc-daemon
CHANGED
@@ -17,6 +17,7 @@ $tasks_copy_threads = {} # copy threads by storage name
|
|
17
17
|
$tasks_delete_threads = {} # delete threads by storage name
|
18
18
|
$check_threads = {} # check threads by storage name
|
19
19
|
$copy_count = 0 # copy tasks count for current host
|
20
|
+
$copy_speed = 0 # copy tasks speed sum for current host
|
20
21
|
$exit_signal = false
|
21
22
|
$global_daemon_thread = nil
|
22
23
|
$update_tasks_thread = nil
|
data/bin/fc-manage
CHANGED
@@ -64,6 +64,14 @@ Command:
|
|
64
64
|
list show all FC::Var-s
|
65
65
|
show <variable> show current value for variable
|
66
66
|
change <variable> change variable
|
67
|
+
}],
|
68
|
+
'copy_speed' => [
|
69
|
+
'show and change copy speed limit for FC hosts',
|
70
|
+
%q{Usage: fc-manage [options] copy_speed <command>
|
71
|
+
Command:
|
72
|
+
list show all limits
|
73
|
+
change <host> change current copy speed limit for host
|
74
|
+
add add copy speed limit for host
|
67
75
|
}],
|
68
76
|
'item' => [
|
69
77
|
'show and manage items',
|
@@ -79,8 +87,8 @@ desc = %q{Get info and manage for storages, policies and items.
|
|
79
87
|
Usage: fc-manage [options] <command> [<args>]
|
80
88
|
Commands:
|
81
89
|
}
|
82
|
-
commands_help.each{|key, val| desc << " #{key}#{" "*(
|
83
|
-
desc << " help
|
90
|
+
commands_help.each{|key, val| desc << " #{key}#{" "*(11-key.size)}#{val[0]}\n"}
|
91
|
+
desc << " help show help for commands ('fc-manage help <command>')\n"
|
84
92
|
$options = option_parser_init(descriptions, desc)
|
85
93
|
FC::Storage.instance_variable_set(:@uname, $options[:curr_host]) if $options[:curr_host] && $options[:curr_host] != FC::Storage.curr_host
|
86
94
|
trap("INT", proc {exit})
|
data/bin/fc-setup-db
CHANGED
@@ -8,22 +8,35 @@ require 'utils'
|
|
8
8
|
require 'readline'
|
9
9
|
|
10
10
|
descriptions = {
|
11
|
-
:host => {:short => 'h', :full => 'host',
|
12
|
-
:database => {:short => 'd', :full => 'db',
|
13
|
-
:username => {:short => 'u', :full => 'user',
|
14
|
-
:password => {:short => 'p', :full => 'password',
|
15
|
-
:port => {:short => 'P', :full => 'port',
|
16
|
-
:prefix => {:short => 't', :full => 'prefix',
|
17
|
-
:init_tables =>{:short => 'i', :full => 'init',
|
18
|
-
:force => {:short => 'f', :full => 'force',
|
11
|
+
:host => {:short => 'h', :full => 'host', :default => 'localhost', :text => 'mysql host name, default "localhost"', :save => true},
|
12
|
+
:database => {:short => 'd', :full => 'db', :default => 'fc', :text => 'mysql database, default "fc"', :save => true},
|
13
|
+
:username => {:short => 'u', :full => 'user', :default => 'root', :text => 'mysql user, default "root"', :save => true},
|
14
|
+
:password => {:short => 'p', :full => 'password', :default => '', :text => 'mysql password, default ""', :save => true},
|
15
|
+
:port => {:short => 'P', :full => 'port', :default => '3306', :text => 'mysql port, default "3306"', :save => true},
|
16
|
+
:prefix => {:short => 't', :full => 'prefix', :default => '', :text => 'tables prefix, default ""', :save => true},
|
17
|
+
:init_tables =>{:short => 'i', :full => 'init', :default => false, :text => 'init tables, default no', :no_val => true},
|
18
|
+
:force => {:short => 'f', :full => 'force', :default => false, :text => 'do not ask questions', :no_val => true},
|
19
|
+
:migrations => {:short => 'm', :full => 'migrations',:default => false, :text => 'Make not ask questions', :no_val => true}
|
19
20
|
}
|
20
21
|
desc = %q{Setup FileCluster database connection options.
|
21
|
-
Create tables
|
22
|
+
Create tables on --init.
|
23
|
+
Make database migrations on --migrations.
|
24
|
+
If no host, database, username, password, port, prefix try to use current db.yml.
|
22
25
|
Usage: fc-init-db [options]}
|
23
26
|
options = option_parser_init(descriptions, desc)
|
24
|
-
options.delete('optparse')
|
25
27
|
trap("INT", proc {exit})
|
26
28
|
|
29
|
+
if !options[:__keys][:host] && !options[:__keys][:database] && !options[:__keys][:username] && !options[:__keys][:password] && !options[:__keys][:port] && !options[:__keys][:prefix]
|
30
|
+
default_db_config = File.expand_path(File.dirname(__FILE__))+'/db.yml'
|
31
|
+
if File.exists?(default_db_config)
|
32
|
+
db_options = Psych.load(File.read(default_db_config))
|
33
|
+
options.merge!(db_options)
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
options.delete('optparse')
|
38
|
+
options.delete(:__keys)
|
39
|
+
|
27
40
|
puts options.inspect.gsub(/[\{\}\:]/, "").gsub(", ", "\n").gsub(/(.{7,})=>/, "\\1:\t").gsub("=>", ":\t\t")
|
28
41
|
|
29
42
|
s = options[:force] ? 'y' : Readline.readline("Continue? (y/n) ", false).strip.downcase
|
@@ -37,12 +50,19 @@ if s == "y" || s == "yes"
|
|
37
50
|
FC::DB.init_db
|
38
51
|
puts "ok"
|
39
52
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
53
|
+
unless default_db_config
|
54
|
+
print "Save to config.. "
|
55
|
+
options.select!{|key, val| descriptions[key][:save]}
|
56
|
+
File.open(File.expand_path(File.dirname(__FILE__))+'/db.yml', 'w') do |f|
|
57
|
+
f.write(options.to_yaml)
|
58
|
+
end
|
59
|
+
puts "ok"
|
60
|
+
end
|
61
|
+
if options[:migrations]
|
62
|
+
print "Make migrations.. "
|
63
|
+
FC::DB.migrations
|
64
|
+
puts "ok"
|
44
65
|
end
|
45
|
-
puts "ok"
|
46
66
|
else
|
47
67
|
puts "Canceled."
|
48
68
|
end
|
@@ -4,7 +4,7 @@ class CopyTaskThread < BaseThread
|
|
4
4
|
Thread.current[:tasks_processed] = 0 unless Thread.current[:tasks_processed]
|
5
5
|
while task = $tasks_copy[storage_name].shift do
|
6
6
|
$curr_tasks << task
|
7
|
-
$log.debug("CopyTaskThread(#{storage_name}): run task for item_storage ##{task.id}, copy_count=#{$copy_count}")
|
7
|
+
$log.debug("CopyTaskThread(#{storage_name}): run task for item_storage ##{task.id}, copy_count=#{$copy_count}, copy_speed=#{$copy_speed}")
|
8
8
|
make_copy(task)
|
9
9
|
$curr_tasks.delete(task)
|
10
10
|
$log.debug("CopyTaskThread(#{storage_name}): finish task for item_storage ##{task.id}")
|
@@ -15,7 +15,18 @@ class CopyTaskThread < BaseThread
|
|
15
15
|
|
16
16
|
def make_copy(task)
|
17
17
|
sleep 0.1 while $copy_count > FC::Var.get('daemon_copy_tasks_per_host_limit', 10).to_i
|
18
|
+
limit = FC::Var.get_current_speed_limit
|
19
|
+
speed_limit = nil
|
20
|
+
if limit
|
21
|
+
if $copy_count == 0
|
22
|
+
speed_limit = (limit - $copy_speed) / 0.75
|
23
|
+
else
|
24
|
+
sleep 0.1 while (speed_limit = (limit - $copy_speed) / ($copy_count + 0.4)) < limit*0.1
|
25
|
+
end
|
26
|
+
end
|
18
27
|
$copy_count += 1
|
28
|
+
$copy_speed += speed_limit if speed_limit
|
29
|
+
|
19
30
|
storage = $storages.detect{|s| s.name == task.storage_name}
|
20
31
|
begin
|
21
32
|
item = FC::Item.find(task.item_id)
|
@@ -35,11 +46,12 @@ class CopyTaskThread < BaseThread
|
|
35
46
|
end
|
36
47
|
src_storage = $all_storages.detect{|s| s.name == src_item_storage.storage_name}
|
37
48
|
$log.debug("Copy from #{src_storage.name} to #{storage.name} #{storage.path}#{item.name}")
|
38
|
-
item.copy_item_storage(src_storage, storage, task)
|
49
|
+
item.copy_item_storage(src_storage, storage, task, speed_limit)
|
39
50
|
rescue Exception => e
|
40
51
|
error "Copy item_storage error: #{e.message}; #{e.backtrace.join(', ')}", :item_id => task.item_id, :item_storage_id => task.id
|
41
52
|
$curr_tasks.delete(task)
|
42
53
|
ensure
|
43
54
|
$copy_count -= 1 if $copy_count > 0
|
55
|
+
$copy_speed -= speed_limit if speed_limit
|
44
56
|
end
|
45
57
|
end
|
data/lib/fc/copy_rule.rb
CHANGED
@@ -30,14 +30,10 @@ module FC
|
|
30
30
|
|
31
31
|
# get available storage for copy
|
32
32
|
def self.get_proper_storage_for_copy(options)
|
33
|
-
exclude = options[:exclude] || []
|
34
33
|
rules = check_all(options[:item_id].to_i, options[:size].to_i, options[:item_copies].to_i, options[:name].to_s, options[:tag].to_s, options[:dir] ? true : false, options[:src_storage])
|
35
34
|
result = nil
|
36
|
-
rules.
|
37
|
-
result = rule.get_copy_storages.
|
38
|
-
!exclude.include?(storage.name) && storage.up? && storage.size + options[:size].to_i < storage.size_limit
|
39
|
-
end.first
|
40
|
-
break if result
|
35
|
+
rules.detect do |rule|
|
36
|
+
result = FC::Storage.select_proper_storage_for_create(rule.get_copy_storages, options[:size].to_i, options[:exclude] || [])
|
41
37
|
end
|
42
38
|
result
|
43
39
|
end
|
data/lib/fc/db.rb
CHANGED
@@ -6,13 +6,27 @@ module FC
|
|
6
6
|
|
7
7
|
def self.connect_by_config(options)
|
8
8
|
@options = options.clone
|
9
|
-
@options[:port] = options[:port].to_i if options[:port]
|
10
|
-
@prefix = options[:prefix].to_s if options[:prefix]
|
11
|
-
@connects = {}
|
9
|
+
@options[:port] = @options[:port].to_i if @options[:port]
|
10
|
+
@prefix = @options[:prefix].to_s if @options[:prefix]
|
11
|
+
@connects = {} unless @connects
|
12
12
|
@connects[Thread.current.object_id] = Mysql2::Client.new(@options)
|
13
13
|
end
|
14
14
|
|
15
|
-
def self.connect
|
15
|
+
def self.connect(options = {})
|
16
|
+
if !@options
|
17
|
+
if defined?(ActiveRecord::Base) && ActiveRecord::Base.connection
|
18
|
+
connection = ActiveRecord::Base.connection.instance_variable_get(:@connection)
|
19
|
+
@options = connection.query_options.clone
|
20
|
+
@options.merge!(options)
|
21
|
+
@prefix = @options[:prefix].to_s if @options[:prefix]
|
22
|
+
@connects = {} unless @connects
|
23
|
+
@connects[Thread.current.object_id] = connection
|
24
|
+
else
|
25
|
+
self.connect_by_config(options)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
@options.merge!(options)
|
29
|
+
end
|
16
30
|
if @options[:multi_threads]
|
17
31
|
@connects[Thread.current.object_id] ||= Mysql2::Client.new(@options)
|
18
32
|
else
|
@@ -20,9 +34,18 @@ module FC
|
|
20
34
|
end
|
21
35
|
end
|
22
36
|
|
37
|
+
def self.connect!(options = {})
|
38
|
+
self.connect(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
# deprecated!
|
23
42
|
def self.connect=(connect, options = {})
|
24
43
|
self.connect_by_config connect.query_options.merge(options).merge(:as => :hash)
|
25
44
|
end
|
45
|
+
class << self
|
46
|
+
extend Gem::Deprecate
|
47
|
+
deprecate :connect=, :connect!, 2016, 01
|
48
|
+
end
|
26
49
|
|
27
50
|
def self.close
|
28
51
|
if @options[:multi_threads]
|
@@ -37,7 +60,9 @@ module FC
|
|
37
60
|
|
38
61
|
# connect.query with deadlock solution
|
39
62
|
def self.query(sql)
|
40
|
-
FC::DB.connect.query(sql)
|
63
|
+
r = FC::DB.connect.query(sql)
|
64
|
+
r = r.each(:as => :hash){} if r
|
65
|
+
r
|
41
66
|
rescue Mysql2::Error => e
|
42
67
|
if e.message.match('Deadlock found when trying to get lock')
|
43
68
|
puts "Deadlock"
|
@@ -51,14 +76,14 @@ module FC
|
|
51
76
|
else
|
52
77
|
raise e
|
53
78
|
end
|
54
|
-
end
|
79
|
+
end
|
55
80
|
|
56
81
|
def self.server_time
|
57
82
|
FC::DB.query("SELECT UNIX_TIMESTAMP() as curr_time").first['curr_time'].to_i
|
58
83
|
end
|
59
84
|
|
60
|
-
def self.init_db
|
61
|
-
FC::DB.
|
85
|
+
def self.init_db(silent = false)
|
86
|
+
FC::DB.query(%{
|
62
87
|
CREATE TABLE #{@prefix}items (
|
63
88
|
id int NOT NULL AUTO_INCREMENT,
|
64
89
|
name varchar(1024) NOT NULL DEFAULT '',
|
@@ -78,10 +103,10 @@ module FC
|
|
78
103
|
proc_time = %{
|
79
104
|
SET NEW.time = UNIX_TIMESTAMP();
|
80
105
|
}
|
81
|
-
FC::DB.
|
82
|
-
FC::DB.
|
106
|
+
FC::DB.query("CREATE TRIGGER fc_items_before_insert BEFORE INSERT on #{@prefix}items FOR EACH ROW BEGIN #{proc_time} END")
|
107
|
+
FC::DB.query("CREATE TRIGGER fc_items_before_update BEFORE UPDATE on #{@prefix}items FOR EACH ROW BEGIN #{proc_time} END")
|
83
108
|
|
84
|
-
FC::DB.
|
109
|
+
FC::DB.query(%{
|
85
110
|
CREATE TABLE #{@prefix}storages (
|
86
111
|
id int NOT NULL AUTO_INCREMENT,
|
87
112
|
name varchar(255) NOT NULL DEFAULT '',
|
@@ -108,10 +133,10 @@ module FC
|
|
108
133
|
#{proc}
|
109
134
|
END IF;
|
110
135
|
}
|
111
|
-
FC::DB.
|
112
|
-
FC::DB.
|
136
|
+
FC::DB.query("CREATE TRIGGER fc_storages_after_delete AFTER DELETE on #{@prefix}storages FOR EACH ROW BEGIN #{proc} END")
|
137
|
+
FC::DB.query("CREATE TRIGGER fc_storages_after_update AFTER UPDATE on #{@prefix}storages FOR EACH ROW BEGIN #{proc_update} END")
|
113
138
|
|
114
|
-
FC::DB.
|
139
|
+
FC::DB.query(%{
|
115
140
|
CREATE TABLE #{@prefix}policies (
|
116
141
|
id int NOT NULL AUTO_INCREMENT,
|
117
142
|
name varchar(255) NOT NULL DEFAULT '',
|
@@ -125,10 +150,10 @@ module FC
|
|
125
150
|
SELECT GROUP_CONCAT(name ORDER BY FIND_IN_SET(name, NEW.create_storages)) INTO @create_storages_list FROM #{@prefix}storages WHERE FIND_IN_SET(name, NEW.create_storages);
|
126
151
|
SET NEW.create_storages = @create_storages_list;
|
127
152
|
}
|
128
|
-
FC::DB.
|
129
|
-
FC::DB.
|
153
|
+
FC::DB.query("CREATE TRIGGER fc_policies_before_insert BEFORE INSERT on #{@prefix}policies FOR EACH ROW BEGIN #{proc} END")
|
154
|
+
FC::DB.query("CREATE TRIGGER fc_policies_before_update BEFORE UPDATE on #{@prefix}policies FOR EACH ROW BEGIN #{proc} END")
|
130
155
|
|
131
|
-
FC::DB.
|
156
|
+
FC::DB.query(%{
|
132
157
|
CREATE TABLE #{@prefix}items_storages (
|
133
158
|
id int NOT NULL AUTO_INCREMENT,
|
134
159
|
item_id int DEFAULT NULL,
|
@@ -165,13 +190,13 @@ module FC
|
|
165
190
|
#{proc.gsub('NEW', 'OLD')}
|
166
191
|
UPDATE #{@prefix}storages SET size=size-@item_size WHERE name = OLD.storage_name;
|
167
192
|
}
|
168
|
-
FC::DB.
|
169
|
-
FC::DB.
|
170
|
-
FC::DB.
|
171
|
-
FC::DB.
|
172
|
-
FC::DB.
|
193
|
+
FC::DB.query("CREATE TRIGGER fc_items_storages_before_insert BEFORE INSERT on #{@prefix}items_storages FOR EACH ROW BEGIN #{proc_time} END")
|
194
|
+
FC::DB.query("CREATE TRIGGER fc_items_storages_before_update BEFORE UPDATE on #{@prefix}items_storages FOR EACH ROW BEGIN #{proc_time} END")
|
195
|
+
FC::DB.query("CREATE TRIGGER fc_items_storages_after_update AFTER UPDATE on #{@prefix}items_storages FOR EACH ROW BEGIN #{proc} END")
|
196
|
+
FC::DB.query("CREATE TRIGGER fc_items_storages_after_insert AFTER INSERT on #{@prefix}items_storages FOR EACH ROW BEGIN #{proc_add} END")
|
197
|
+
FC::DB.query("CREATE TRIGGER fc_items_storages_after_delete AFTER DELETE on #{@prefix}items_storages FOR EACH ROW BEGIN #{proc_del} END")
|
173
198
|
|
174
|
-
FC::DB.
|
199
|
+
FC::DB.query(%{
|
175
200
|
CREATE TABLE #{@prefix}errors (
|
176
201
|
id int NOT NULL AUTO_INCREMENT,
|
177
202
|
item_id int DEFAULT NULL,
|
@@ -182,9 +207,9 @@ module FC
|
|
182
207
|
PRIMARY KEY (id), KEY (item_id), KEY (item_storage_id), KEY (host), KEY (time)
|
183
208
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
|
184
209
|
})
|
185
|
-
FC::DB.
|
210
|
+
FC::DB.query("CREATE TRIGGER fc_errors_before_insert BEFORE INSERT on #{@prefix}errors FOR EACH ROW BEGIN #{proc_time} END")
|
186
211
|
|
187
|
-
FC::DB.
|
212
|
+
FC::DB.query(%{
|
188
213
|
CREATE TABLE #{@prefix}copy_rules (
|
189
214
|
id int NOT NULL AUTO_INCREMENT,
|
190
215
|
copy_storages text NOT NULL DEFAULT '',
|
@@ -193,7 +218,7 @@ module FC
|
|
193
218
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
|
194
219
|
})
|
195
220
|
|
196
|
-
FC::DB.
|
221
|
+
FC::DB.query(%{
|
197
222
|
CREATE TABLE #{@prefix}vars (
|
198
223
|
name varchar(255) DEFAULT NULL,
|
199
224
|
val varchar(255) DEFAULT NULL,
|
@@ -202,19 +227,41 @@ module FC
|
|
202
227
|
PRIMARY KEY (name)
|
203
228
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
|
204
229
|
})
|
205
|
-
FC::DB.
|
206
|
-
FC::DB.
|
207
|
-
FC::DB.
|
208
|
-
FC::DB.
|
209
|
-
FC::DB.
|
210
|
-
FC::DB.
|
211
|
-
FC::DB.
|
212
|
-
FC::DB.
|
213
|
-
FC::DB.
|
214
|
-
FC::DB.
|
215
|
-
FC::DB.
|
216
|
-
FC::DB.
|
217
|
-
FC::DB.
|
230
|
+
FC::DB.query("CREATE TRIGGER fc_vars_before_insert BEFORE INSERT on #{@prefix}vars FOR EACH ROW BEGIN #{proc_time} END")
|
231
|
+
FC::DB.query("CREATE TRIGGER fc_vars_before_update BEFORE UPDATE on #{@prefix}vars FOR EACH ROW BEGIN #{proc_time} END")
|
232
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_cycle_time', val='30', descr='time between global daemon checks and storages available checks'")
|
233
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_global_wait_time', val='120', descr='time between runs global daemon if it does not running'")
|
234
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_tasks_copy_group_limit', val='1000', descr='select limit for copy tasks'")
|
235
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_tasks_delete_group_limit', val='10000', descr='select limit for delete tasks'")
|
236
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_tasks_copy_threads_limit', val='10', descr='copy tasks threads count limit for one storage'")
|
237
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_tasks_delete_threads_limit', val='10', descr='delete tasks threads count limit for one storage'")
|
238
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_copy_tasks_per_host_limit', val='10', descr='copy tasks count limit for one host'")
|
239
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_global_tasks_group_limit', val='1000', descr='select limit for create copy tasks'")
|
240
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_global_error_items_ttl', val='86400', descr='ttl for items with error status before delete'")
|
241
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_global_error_items_storages_ttl', val='86400', descr='ttl for items_storages with error status before delete'")
|
242
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_restart_period', val='86400', descr='time between fc-daemon self restart'")
|
243
|
+
|
244
|
+
FC::DB.migrations(silent)
|
245
|
+
end
|
246
|
+
|
247
|
+
def self.version
|
248
|
+
return 1
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.migrations(silent = false)
|
252
|
+
next_version = FC::DB.query("SELECT val FROM #{FC::DB.prefix}vars WHERE name='db_version'").first['val'].to_i + 1 rescue 1
|
253
|
+
while self.respond_to?("migrate_#{next_version}")
|
254
|
+
puts "migrate to #{next_version}" unless silent
|
255
|
+
self.send("migrate_#{next_version}")
|
256
|
+
FC::DB.query("REPLACE #{FC::DB.prefix}vars SET val=#{next_version}, name='db_version'")
|
257
|
+
next_version += 1
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def self.migrate_1
|
262
|
+
FC::DB.query("ALTER TABLE #{@prefix}storages ADD COLUMN url_weight int NOT NULL DEFAULT 0")
|
263
|
+
FC::DB.query("ALTER TABLE #{@prefix}storages ADD COLUMN write_weight int NOT NULL DEFAULT 0")
|
264
|
+
FC::DB.query("INSERT INTO #{@prefix}vars SET name='daemon_copy_speed_per_host_limit', val='', descr='copy tasks speed limit for hosts, change via fc-manage copy_speed'")
|
218
265
|
end
|
219
266
|
end
|
220
267
|
end
|
data/lib/fc/item.rb
CHANGED
@@ -67,19 +67,23 @@ module FC
|
|
67
67
|
def make_item_storage(storage, status = 'new')
|
68
68
|
# new storage_item?
|
69
69
|
item_storage = FC::ItemStorage.where('item_id=? AND storage_name=?', id, storage.name).first
|
70
|
-
|
70
|
+
if item_storage
|
71
|
+
item_storage.delete
|
72
|
+
storage.size = storage.size.to_i - size.to_i
|
73
|
+
end
|
71
74
|
|
72
75
|
item_storage = FC::ItemStorage.new({:item_id => id, :storage_name => storage.name, :status => status})
|
73
76
|
item_storage.save
|
77
|
+
storage.size = storage.size.to_i + size.to_i
|
74
78
|
item_storage
|
75
79
|
end
|
76
80
|
|
77
|
-
def copy_item_storage(src, storage, item_storage, remove_local = false)
|
81
|
+
def copy_item_storage(src, storage, item_storage, remove_local = false, speed_limit = nil)
|
78
82
|
begin
|
79
83
|
if src.instance_of?(FC::Storage)
|
80
|
-
src.copy_to_local(name, "#{storage.path}#{name}")
|
84
|
+
src.copy_to_local(name, "#{storage.path}#{name}", speed_limit)
|
81
85
|
else
|
82
|
-
storage.copy_path(src, name, remove_local)
|
86
|
+
storage.copy_path(src, name, remove_local, speed_limit)
|
83
87
|
end
|
84
88
|
md5_on_storage = storage.md5_sum(name)
|
85
89
|
rescue Exception => e
|
@@ -130,7 +134,24 @@ module FC
|
|
130
134
|
def get_available_storages
|
131
135
|
r = FC::DB.query("SELECT st.* FROM #{FC::Storage.table_name} as st, #{FC::ItemStorage.table_name} as ist WHERE
|
132
136
|
ist.item_id = #{id} AND ist.status='ready' AND ist.storage_name = st.name")
|
133
|
-
r.map{|data| FC::Storage.create_from_fiels(data)}.select {|storage| storage.up? }
|
137
|
+
r.map{|data| FC::Storage.create_from_fiels(data)}.select {|storage| storage.up? && storage.url_weight.to_i >= 0}
|
138
|
+
end
|
139
|
+
|
140
|
+
def urls
|
141
|
+
get_available_storages.map{|storage| File.join(storage.url, name)}
|
142
|
+
end
|
143
|
+
|
144
|
+
def url
|
145
|
+
available_storages = get_available_storages()
|
146
|
+
# sort by random(url_weight)
|
147
|
+
best_storage = available_storages.map{ |storage|
|
148
|
+
[storage, Kernel.rand(storage.url_weight.to_i * 100)]
|
149
|
+
}.sort{ |a, b|
|
150
|
+
a[1] <=> b[1]
|
151
|
+
}.map{|el| el[0]}.last
|
152
|
+
best_storage = available_storages.sample unless best_storage
|
153
|
+
raise "URL find - no avable storage for item #{id}" unless best_storage
|
154
|
+
File.join(best_storage.url, name)
|
134
155
|
end
|
135
156
|
end
|
136
157
|
end
|
data/lib/fc/policy.rb
CHANGED
@@ -28,36 +28,20 @@ module FC
|
|
28
28
|
@create_storages_cache
|
29
29
|
end
|
30
30
|
|
31
|
-
# get available storages for create by size
|
32
|
-
def get_proper_storages_for_create(size, exclude = [])
|
33
|
-
get_create_storages.select do |storage|
|
34
|
-
!exclude.include?(storage.name) && storage.up? && storage.size + size < storage.size_limit
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
31
|
# get available storage for create by size and local item path
|
39
32
|
def get_proper_storage_for_create(size, local_path = nil)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
1
|
48
|
-
elsif local_path && dev == File.stat(b.path).dev
|
49
|
-
-1
|
50
|
-
else
|
51
|
-
a.free_rate <=> b.free_rate
|
52
|
-
end
|
53
|
-
elsif FC::Storage.curr_host == a.host
|
54
|
-
1
|
55
|
-
elsif FC::Storage.curr_host == b.host
|
56
|
-
-1
|
57
|
-
else
|
58
|
-
a.free_rate <=> b.free_rate
|
33
|
+
FC::Storage.select_proper_storage_for_create(get_create_storages, size) do |storages|
|
34
|
+
local_storages = storages.select{|storage| FC::Storage.curr_host == storage.host}
|
35
|
+
# find same storage device as local_path device
|
36
|
+
if local_path && !local_storages.empty?
|
37
|
+
dev = File.stat(local_path).dev
|
38
|
+
dev_storage = local_storages.select{|storage| dev == File.stat(storage.path).dev}.first
|
39
|
+
local_storages = [dev_storage] if dev_storage
|
59
40
|
end
|
60
|
-
|
41
|
+
# if no local storages - use all storages
|
42
|
+
local_storages = storages if local_storages.empty?
|
43
|
+
local_storages
|
44
|
+
end
|
61
45
|
end
|
62
46
|
end
|
63
47
|
end
|
data/lib/fc/storage.rb
CHANGED
@@ -3,7 +3,7 @@ require 'shellwords'
|
|
3
3
|
|
4
4
|
module FC
|
5
5
|
class Storage < DbBase
|
6
|
-
set_table :storages, 'name, host, path, url, size, size_limit, check_time, copy_storages'
|
6
|
+
set_table :storages, 'name, host, path, url, size, size_limit, check_time, copy_storages, url_weight, write_weight'
|
7
7
|
|
8
8
|
class << self
|
9
9
|
attr_accessor :check_time_limit, :storages_cache_time, :get_copy_storages_mutex
|
@@ -16,6 +16,19 @@ module FC
|
|
16
16
|
@uname || @uname = `uname -n`.chomp
|
17
17
|
end
|
18
18
|
|
19
|
+
def self.select_proper_storage_for_create(storages, size, exclude = [])
|
20
|
+
list = storages.select do |storage|
|
21
|
+
!exclude.include?(storage.name) && storage.up? && storage.size + size < storage.size_limit && storage.write_weight.to_i >= 0
|
22
|
+
end
|
23
|
+
list = yield(list) if block_given?
|
24
|
+
# sort by random(free_rate * write_weight)
|
25
|
+
list.map{ |storage|
|
26
|
+
[storage, Kernel.rand(storage.free_rate * (storage.write_weight.to_i == 0 ? 0.01 : storage.write_weight.to_i) * 1000000000)]
|
27
|
+
}.sort{ |a, b|
|
28
|
+
a[1] <=> b[1]
|
29
|
+
}.map{|el| el[0]}.last
|
30
|
+
end
|
31
|
+
|
19
32
|
def initialize(params = {})
|
20
33
|
path = (params['path'] || params[:path])
|
21
34
|
if path && !path.to_s.empty?
|
@@ -35,7 +48,8 @@ module FC
|
|
35
48
|
end
|
36
49
|
|
37
50
|
def free_rate
|
38
|
-
free.to_f / size_limit
|
51
|
+
rate = free.to_f / size_limit
|
52
|
+
rate < 0 ? 0.0 : rate
|
39
53
|
end
|
40
54
|
|
41
55
|
def get_copy_storages
|
@@ -63,7 +77,7 @@ module FC
|
|
63
77
|
end
|
64
78
|
|
65
79
|
# copy local_path to storage
|
66
|
-
def copy_path(local_path, file_name, try_move = false)
|
80
|
+
def copy_path(local_path, file_name, try_move = false, speed_limit = nil)
|
67
81
|
dst_path = "#{self.path}#{file_name}"
|
68
82
|
|
69
83
|
cmd = "rm -rf #{dst_path.shellescape}; mkdir -p #{File.dirname(dst_path).shellescape}"
|
@@ -72,23 +86,25 @@ module FC
|
|
72
86
|
raise r if $?.exitstatus != 0
|
73
87
|
|
74
88
|
op = try_move && self.class.curr_host == host && File.stat(local_path).dev == File.stat(File.dirname(dst_path)).dev ? 'mv' : 'cp -r'
|
89
|
+
speed_limit = (speed_limit * 1000).to_i if speed_limit.to_i > 0
|
75
90
|
cmd = self.class.curr_host == host ?
|
76
91
|
"#{op} #{local_path.shellescape} #{dst_path.shellescape}" :
|
77
|
-
"scp -r -q -oBatchMode=yes -oStrictHostKeyChecking=no #{local_path.shellescape} #{self.host}:\"#{dst_path.shellescape}\""
|
92
|
+
"scp -r -q -oBatchMode=yes -oStrictHostKeyChecking=no #{speed_limit.to_i > 0 ? '-l '+speed_limit.to_s : ''} #{local_path.shellescape} #{self.host}:\"#{dst_path.shellescape}\""
|
78
93
|
r = `#{cmd} 2>&1`
|
79
94
|
raise r if $?.exitstatus != 0
|
80
95
|
end
|
81
96
|
|
82
97
|
# copy object to local_path
|
83
|
-
def copy_to_local(file_name, local_path)
|
98
|
+
def copy_to_local(file_name, local_path, speed_limit = nil)
|
84
99
|
src_path = "#{self.path}#{file_name}"
|
85
100
|
|
86
101
|
r = `rm -rf #{local_path.shellescape}; mkdir -p #{File.dirname(local_path).shellescape} 2>&1`
|
87
102
|
raise r if $?.exitstatus != 0
|
88
103
|
|
104
|
+
speed_limit = (speed_limit * 1000).to_i if speed_limit.to_i > 0
|
89
105
|
cmd = self.class.curr_host == host ?
|
90
106
|
"cp -r #{src_path.shellescape} #{local_path.shellescape}" :
|
91
|
-
"scp -r -q -oBatchMode=yes -oStrictHostKeyChecking=no #{self.host}:\"#{src_path.shellescape}\" #{local_path.shellescape}"
|
107
|
+
"scp -r -q -oBatchMode=yes -oStrictHostKeyChecking=no #{speed_limit.to_i > 0 ? '-l '+speed_limit.to_s : ''} #{self.host}:\"#{src_path.shellescape}\" #{local_path.shellescape}"
|
92
108
|
r = `#{cmd} 2>&1`
|
93
109
|
raise r if $?.exitstatus != 0
|
94
110
|
end
|
@@ -102,6 +118,7 @@ module FC
|
|
102
118
|
r = `#{cmd} 2>&1`
|
103
119
|
raise r if $?.exitstatus != 0
|
104
120
|
|
121
|
+
speed_limit = (speed_limit * 1000).to_i if speed_limit.to_i > 0
|
105
122
|
cmd = self.class.curr_host == host ?
|
106
123
|
"ls -la #{dst_path.shellescape}" :
|
107
124
|
"ssh -q -oBatchMode=yes -oStrictHostKeyChecking=no #{self.host} \"ls -la #{dst_path.shellescape}\""
|
@@ -134,9 +151,7 @@ module FC
|
|
134
151
|
|
135
152
|
# get available storage for copy by size
|
136
153
|
def get_proper_storage_for_copy(size, exclude = [])
|
137
|
-
get_copy_storages
|
138
|
-
!exclude.include?(storage.name) && storage.up? && storage.size + size < storage.size_limit
|
139
|
-
end.first
|
154
|
+
FC::Storage.select_proper_storage_for_create(get_copy_storages, size, exclude)
|
140
155
|
end
|
141
156
|
end
|
142
157
|
end
|
data/lib/fc/var.rb
CHANGED
@@ -35,5 +35,28 @@ module FC
|
|
35
35
|
end
|
36
36
|
@all_vars
|
37
37
|
end
|
38
|
+
|
39
|
+
def self.get_speed_limits
|
40
|
+
limits = {
|
41
|
+
'all' => nil
|
42
|
+
}
|
43
|
+
list = self.get('daemon_copy_speed_per_host_limit', '').to_s
|
44
|
+
limits.merge! Hash[list.split(';;').map{|v| v.split('::')}]
|
45
|
+
limits.each{|host, val| limits[host] = val.to_f > 0 ? val.to_f : nil }
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.set_speed_limit(host, val)
|
49
|
+
limits = self.get_speed_limits
|
50
|
+
limits[host.to_s] = val.to_f
|
51
|
+
list = limits.map{|h, v| "#{h}::#{v}"}.join(';;')
|
52
|
+
self.set('daemon_copy_speed_per_host_limit', list)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.get_current_speed_limit
|
56
|
+
limits = self.get_speed_limits
|
57
|
+
limit = limits[FC::Storage.curr_host]
|
58
|
+
limit = limits['all'] unless limit
|
59
|
+
limit
|
60
|
+
end
|
38
61
|
end
|
39
62
|
end
|
data/lib/fc/version.rb
CHANGED
data/lib/manage.rb
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
def copy_speed_list
|
5
|
+
FC::Var.get_speed_limits.each do |name, val|
|
6
|
+
puts name.to_s+(val ? " - limit: #{val}Mbit" : " - unlimit")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def copy_speed_add
|
11
|
+
hosts = ['all'] + all_hosts
|
12
|
+
puts "Add copy speed limit"
|
13
|
+
begin
|
14
|
+
host = stdin_read_val("Host (default #{FC::Storage.curr_host})", true).strip
|
15
|
+
host = FC::Storage.curr_host if host.empty?
|
16
|
+
puts "Host can be one of: #{hosts.join(', ')}" unless hosts.index(host)
|
17
|
+
end until hosts.index(host)
|
18
|
+
limit = stdin_read_val("Speed limit, Mbit/s (default 0 - unlimit)", true).to_f
|
19
|
+
puts %Q{\nCopy speed limit
|
20
|
+
Host: #{host}
|
21
|
+
Speed limit: #{limit > 0 ? limit : 'unlimit'}}
|
22
|
+
s = Readline.readline("Continue? (y/n) ", false).strip.downcase
|
23
|
+
puts ""
|
24
|
+
if s == "y" || s == "yes"
|
25
|
+
begin
|
26
|
+
FC::Var.set_speed_limit(host, limit)
|
27
|
+
rescue Exception => e
|
28
|
+
puts "Error: #{e.message}"
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
puts "ok"
|
32
|
+
else
|
33
|
+
puts "Canceled."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def copy_speed_change
|
38
|
+
if host = find_host
|
39
|
+
puts "Change copy speed limit for host #{host}"
|
40
|
+
curr_limit = FC::Var.get_speed_limits[host]
|
41
|
+
limit = stdin_read_val("Speed limit, Mbit/s (now #{curr_limit ? curr_limit.to_s+', 0 to unlimit' : 'unlimit'})", true)
|
42
|
+
puts limit.to_f
|
43
|
+
puts limit == ''
|
44
|
+
limit = limit == '' ? curr_limit : limit.to_f
|
45
|
+
puts %Q{\nCopy speed limit
|
46
|
+
Host: #{host}
|
47
|
+
Speed limit: #{limit > 0 ? limit : 'unlimit'}}
|
48
|
+
s = Readline.readline("Continue? (y/n) ", false).strip.downcase
|
49
|
+
puts ""
|
50
|
+
if s == "y" || s == "yes"
|
51
|
+
begin
|
52
|
+
FC::Var.set_speed_limit(host, limit)
|
53
|
+
rescue Exception => e
|
54
|
+
puts "Error: #{e.message}"
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
puts "ok"
|
58
|
+
else
|
59
|
+
puts "Canceled."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def all_hosts
|
67
|
+
FC::Storage.where('1').map(&:host).uniq
|
68
|
+
end
|
69
|
+
|
70
|
+
def find_host
|
71
|
+
host = ARGV[2].to_s.strip
|
72
|
+
puts "Storage with host #{host} not found." unless (['all'] + all_hosts).index(host)
|
73
|
+
host
|
74
|
+
end
|
data/lib/manage/storages.rb
CHANGED
@@ -24,6 +24,8 @@ def storages_show
|
|
24
24
|
Host: #{storage.host}
|
25
25
|
Path: #{storage.path}
|
26
26
|
Url: #{storage.url}
|
27
|
+
Url weight: #{storage.url_weight}
|
28
|
+
Write weight #{storage.write_weight}
|
27
29
|
Size: #{size_to_human storage.size} (#{(storage.size_rate*100).to_i}%)
|
28
30
|
Free: #{size_to_human storage.free} (#{(storage.free_rate*100).to_i}%)
|
29
31
|
Size limit: #{size_to_human storage.size_limit}
|
@@ -40,6 +42,8 @@ def storages_add
|
|
40
42
|
name = stdin_read_val('Name')
|
41
43
|
path = stdin_read_val('Path')
|
42
44
|
url = stdin_read_val('Url')
|
45
|
+
url_weight = stdin_read_val('URL weight', true).to_i
|
46
|
+
write_weight = stdin_read_val('Write weight', true).to_i
|
43
47
|
size_limit = human_to_size stdin_read_val('Size limit') {|val| "Size limit not is valid size." unless human_to_size(val)}
|
44
48
|
copy_storages = stdin_read_val('Copy storages', true)
|
45
49
|
storages = FC::Storage.where.map(&:name)
|
@@ -47,7 +51,7 @@ def storages_add
|
|
47
51
|
begin
|
48
52
|
path = path +'/' unless path[-1] == '/'
|
49
53
|
path = '/' + path unless path[0] == '/'
|
50
|
-
storage = FC::Storage.new(:name => name, :host => host, :path => path, :url => url, :size_limit => size_limit, :copy_storages => copy_storages)
|
54
|
+
storage = FC::Storage.new(:name => name, :host => host, :path => path, :url => url, :size_limit => size_limit, :copy_storages => copy_storages, :url_weight => url_weight, :write_weight => write_weight)
|
51
55
|
print "Calc current size.. "
|
52
56
|
size = storage.file_size('', true)
|
53
57
|
puts "ok"
|
@@ -61,6 +65,8 @@ def storages_add
|
|
61
65
|
Host: #{host}
|
62
66
|
Path: #{path}
|
63
67
|
Url: #{url}
|
68
|
+
URL weight: #{url_weight}
|
69
|
+
Write weight: #{write_weight}
|
64
70
|
Size: #{size_to_human size} (#{(size.to_f*100 / size_limit).to_i}%)
|
65
71
|
Free: #{size_to_human free} (#{(free.to_f*100 / size_limit).to_i}%)
|
66
72
|
Size limit: #{size_to_human size_limit}
|
@@ -117,6 +123,8 @@ def storages_change
|
|
117
123
|
host = stdin_read_val("Host (now #{storage.host})", true)
|
118
124
|
path = stdin_read_val("Path (now #{storage.path})", true)
|
119
125
|
url = stdin_read_val("Url (now #{storage.url})", true)
|
126
|
+
url_weight = stdin_read_val("URL weight (now #{storage.url_weight})", true)
|
127
|
+
write_weight = stdin_read_val("Write weight (now #{storage.write_weight})", true)
|
120
128
|
size_limit = stdin_read_val("Size (now #{size_to_human(storage.size_limit)})", true) {|val| "Size limit not is valid size." if !val.empty? && !human_to_size(val)}
|
121
129
|
copy_storages = stdin_read_val("Copy storages (now #{storage.copy_storages})", true)
|
122
130
|
|
@@ -130,6 +138,8 @@ def storages_change
|
|
130
138
|
puts "ok"
|
131
139
|
end
|
132
140
|
storage.url = url unless url.empty?
|
141
|
+
storage.url_weight = url_weight.to_i unless url_weight.empty?
|
142
|
+
storage.write_weight = write_weight.to_i unless write_weight.empty?
|
133
143
|
storage.size_limit = human_to_size(size_limit) unless size_limit.empty?
|
134
144
|
storages = FC::Storage.where.map(&:name)
|
135
145
|
storage.copy_storages = copy_storages.split(',').select{|s| storages.member?(s.strip)}.join(',').strip unless copy_storages.empty?
|
@@ -139,6 +149,8 @@ def storages_change
|
|
139
149
|
Host: #{storage.host}
|
140
150
|
Path: #{storage.path}
|
141
151
|
Url: #{storage.url}
|
152
|
+
URL weight: #{storage.url_weight}
|
153
|
+
Write weight: #{storage.write_weight}
|
142
154
|
Size: #{size_to_human storage.size} (#{(storage.size_rate*100).to_i}%)
|
143
155
|
Free: #{size_to_human storage.free} (#{(storage.free_rate*100).to_i}%)
|
144
156
|
Size limit: #{size_to_human storage.size_limit}
|
data/lib/utils.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
def option_parser_init(descriptions, text)
|
2
|
-
options = {
|
2
|
+
options = {
|
3
|
+
:__keys => {}
|
4
|
+
}
|
3
5
|
optparse = OptionParser.new do |opts|
|
4
6
|
opts.banner = text
|
5
7
|
opts.separator "Options:"
|
6
8
|
|
7
9
|
descriptions.each_entry do |key, desc|
|
8
10
|
options[key] = desc[:default]
|
9
|
-
opts.on("-#{desc[:short]}", "--#{desc[:full]}#{desc[:no_val] ? '' : '='+desc[:full].upcase}", desc[:text])
|
11
|
+
opts.on("-#{desc[:short]}", "--#{desc[:full]}#{desc[:no_val] ? '' : '='+desc[:full].upcase}", desc[:text]) do |s|
|
12
|
+
options[:__keys][key] = s
|
13
|
+
options[key] = s
|
14
|
+
end
|
10
15
|
end
|
11
16
|
opts.on_tail("-?", "--help", "Show this message") do
|
12
17
|
puts opts
|
@@ -25,9 +30,11 @@ end
|
|
25
30
|
def size_to_human(size)
|
26
31
|
return "0" if size == 0
|
27
32
|
units = %w{B KB MB GB TB}
|
33
|
+
minus = size < 0
|
34
|
+
size = -1 * size if minus
|
28
35
|
e = (Math.log(size)/Math.log(1024)).floor
|
29
36
|
s = "%.2f" % (size.to_f / 1024**e)
|
30
|
-
s.sub(/\.?0*$/, units[e])
|
37
|
+
(minus ? '-' : '')+s.sub(/\.?0*$/, units[e])
|
31
38
|
end
|
32
39
|
|
33
40
|
def human_to_size(size)
|
data/test/copy_rule_test.rb
CHANGED
@@ -75,6 +75,15 @@ class CopyRuleTest < Test::Unit::TestCase
|
|
75
75
|
@@storages[1].update_check_time
|
76
76
|
assert_equal 'rec2-sda', FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test', :size => 5).name, 'second storages up, small file'
|
77
77
|
assert_equal 'rec1-sda', FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test', :size => 20).name, 'second storages up, big file'
|
78
|
-
assert_nil FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test', :size => 1000), 'second storages up, big file'
|
78
|
+
assert_nil FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test', :size => 1000), 'second storages up, very big file'
|
79
|
+
@@storages[1].write_weight = 100
|
80
|
+
@@storages[1].save
|
81
|
+
@@storages[2].update_check_time
|
82
|
+
assert_equal 'rec2-sda', FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test', :size => 1).name, 'all storages up, choose by weight'
|
83
|
+
@@storages[1].write_weight = -1
|
84
|
+
@@storages[1].save
|
85
|
+
@@storages[2].write_weight = -1
|
86
|
+
@@storages[2].save
|
87
|
+
assert_equal 'rec1-sda', FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test', :size => 20).name, 'all storages up, 2 disabled by weight, work second rule'
|
79
88
|
end
|
80
89
|
end
|
data/test/helper.rb
CHANGED
@@ -13,5 +13,5 @@ FC::DB.connect_by_config(:username => TEST_USER, :password => TEST_PASSWORD)
|
|
13
13
|
FC::DB.query("DROP DATABASE IF EXISTS #{TEST_DATABASE}")
|
14
14
|
FC::DB.query("CREATE DATABASE #{TEST_DATABASE}")
|
15
15
|
FC::DB.query("USE #{TEST_DATABASE}")
|
16
|
-
FC::DB.init_db
|
16
|
+
FC::DB.init_db(true)
|
17
17
|
FC::DB.options[:database] = TEST_DATABASE
|
data/test/item_test.rb
CHANGED
@@ -3,18 +3,19 @@ require 'helper'
|
|
3
3
|
class ItemTest < Test::Unit::TestCase
|
4
4
|
class << self
|
5
5
|
def startup
|
6
|
-
@@item = FC::Item.new(:name => 'test item', :policy_id => 1, :size => 150)
|
6
|
+
@@item = FC::Item.new(:name => '/test item', :policy_id => 1, :size => 150)
|
7
7
|
@@item.save
|
8
8
|
|
9
9
|
@@storages = []
|
10
|
-
@@storages << FC::Storage.new(:name => 'rec1-sda', :host => 'rec1')
|
11
|
-
@@storages << FC::Storage.new(:name => 'rec2-sda', :host => 'rec2')
|
10
|
+
@@storages << FC::Storage.new(:name => 'rec1-sda', :host => 'rec1', :url => 'http://rec1/sda/')
|
11
|
+
@@storages << FC::Storage.new(:name => 'rec2-sda', :host => 'rec2', :url => 'http://rec2/sda/')
|
12
12
|
@@item_storages = @@storages.map do |storage|
|
13
13
|
storage.save
|
14
14
|
item_storage = FC::ItemStorage.new(:item_id => @@item.id, :storage_name => storage.name, :status => 'ready')
|
15
15
|
item_storage.save
|
16
16
|
item_storage
|
17
17
|
end
|
18
|
+
@@storages << FC::Storage.new(:name => 'rec3-sda', :host => 'rec3', :url => 'http://rec3/sda/')
|
18
19
|
end
|
19
20
|
def shutdown
|
20
21
|
FC::DB.query("DELETE FROM items_storages")
|
@@ -29,7 +30,7 @@ class ItemTest < Test::Unit::TestCase
|
|
29
30
|
assert_raise(ArgumentError) { FC::Item.create_from_local '/bla/bla' }
|
30
31
|
assert_raise(ArgumentError) { FC::Item.create_from_local '/bla/bla', 'test' }
|
31
32
|
assert_raise(RuntimeError) { FC::Item.create_from_local '/bla/bla', 'test', {}}
|
32
|
-
assert_raise(
|
33
|
+
assert_raise() { FC::Item.create_from_local '/bla/bla/bla', 'test', policy}
|
33
34
|
end
|
34
35
|
|
35
36
|
should "mark_deleted" do
|
@@ -42,13 +43,42 @@ class ItemTest < Test::Unit::TestCase
|
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
46
|
+
should "make_item_storage" do
|
47
|
+
storage_size = @@storages[2].size.to_i
|
48
|
+
assert_kind_of FC::ItemStorage, @@item.make_item_storage(@@storages[2])
|
49
|
+
assert_equal storage_size+@@item.size, @@storages[2].size
|
50
|
+
end
|
51
|
+
|
45
52
|
should "get_item_storages" do
|
46
53
|
assert_same_elements @@item_storages.map(&:id), @@item.get_item_storages.map(&:id)
|
47
54
|
end
|
48
55
|
|
49
56
|
should "item get_available_storages" do
|
57
|
+
@@storages.each{|s| s.check_time = 0; s.save}
|
50
58
|
@@storages[0].update_check_time
|
51
59
|
assert_equal 1, @@item.get_available_storages.count
|
52
60
|
assert_equal @@storages[0].name, @@item.get_available_storages.first.name
|
53
61
|
end
|
62
|
+
|
63
|
+
should "item urls" do
|
64
|
+
@@storages.each{|s| s.check_time = 0; s.save}
|
65
|
+
assert_equal 0, @@item.urls.count
|
66
|
+
@@storages.each(&:update_check_time)
|
67
|
+
assert_same_elements ["http://rec1/sda/test item", "http://rec2/sda/test item"], @@item.urls
|
68
|
+
end
|
69
|
+
|
70
|
+
should "item url by url_weight" do
|
71
|
+
@@storages.each(&:update_check_time)
|
72
|
+
@@storages.each{|s| s.url_weight = -1; s.save}
|
73
|
+
assert_raise(RuntimeError) { @@item.url }
|
74
|
+
|
75
|
+
@@storages[0].url_weight = 1
|
76
|
+
@@storages[0].save
|
77
|
+
assert_equal "http://rec1/sda/test item", @@item.url
|
78
|
+
|
79
|
+
@@storages[1].url_weight = 2
|
80
|
+
@@storages[1].save
|
81
|
+
Kernel.stubs(:rand).returns(1)
|
82
|
+
assert_equal "http://rec2/sda/test item", @@item.url
|
83
|
+
end
|
54
84
|
end
|
data/test/policy_test.rb
CHANGED
@@ -64,6 +64,10 @@ class PolicyTest < Test::Unit::TestCase
|
|
64
64
|
assert_equal 'rec2-sdb', @@policy.get_proper_storage_for_create(9, 'test-rec2-sdb').name, 'current host, dev match'
|
65
65
|
@@storages[3].check_time = 0;
|
66
66
|
@@storages[3].save
|
67
|
+
original_rand = Kernel.method(:rand)
|
68
|
+
def Kernel.rand(a)
|
69
|
+
a
|
70
|
+
end
|
67
71
|
assert_equal 'rec2-sdc', @@policy.get_proper_storage_for_create(9, 'test-rec2-sdb').name, 'current host, most free storage'
|
68
72
|
FC::Storage.stubs(:curr_host).returns('rec3')
|
69
73
|
@@storages[5].check_time = 0;
|
@@ -72,6 +76,14 @@ class PolicyTest < Test::Unit::TestCase
|
|
72
76
|
FC::Storage.stubs(:curr_host).returns('rec5')
|
73
77
|
assert_equal 'rec1-sda', @@policy.get_proper_storage_for_create(9, 'test-rec2-sdb').name, 'not current host, most free storage'
|
74
78
|
assert_equal 'rec2-sdc', @@policy.get_proper_storage_for_create(10, 'test-rec2-sdb').name, 'not current host, big file, most free storage with free space'
|
79
|
+
@@storages[4].write_weight = -1;
|
80
|
+
@@storages[4].save
|
81
|
+
assert_equal 'rec3-sdb', @@policy.get_proper_storage_for_create(10, 'test-rec2-sdb').name, 'not current host, big file, most weight storage with free space'
|
82
|
+
@@storages[2].write_weight = 10;
|
83
|
+
@@storages[2].save
|
84
|
+
assert_equal 'rec2-sda', @@policy.get_proper_storage_for_create(10, 'test-rec2-sdb').name, 'not current host, big file, most weight storage with free space'
|
75
85
|
File.unstub(:stat)
|
86
|
+
Kernel.send(:remove_method, :rand)
|
87
|
+
Kernel.define_singleton_method(:rand, original_rand)
|
76
88
|
end
|
77
89
|
end
|
data/test/storage_test.rb
CHANGED
@@ -65,6 +65,8 @@ class StorageTest < Test::Unit::TestCase
|
|
65
65
|
assert_equal 'rec1-sda', @@storages[2].get_proper_storage_for_copy(5).name, 'first storages up'
|
66
66
|
assert_nil @@storages[2].get_proper_storage_for_copy(20), 'first storage full'
|
67
67
|
@@storages[1].update_check_time
|
68
|
+
@@storages[0].write_weight = 100
|
69
|
+
@@storages[0].save
|
68
70
|
assert_equal 'rec1-sda', @@storages[2].get_proper_storage_for_copy(5).name, 'second storages up, small file'
|
69
71
|
assert_equal 'rec2-sda', @@storages[2].get_proper_storage_for_copy(20).name, 'second storages up, big file'
|
70
72
|
assert_nil @@storages[2].get_proper_storage_for_copy(1000), 'second storages up, huge file'
|
metadata
CHANGED
@@ -1,110 +1,97 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: filecluster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
5
|
-
prerelease:
|
4
|
+
version: 0.4.6
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- sh
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2015-
|
11
|
+
date: 2015-04-22 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: mysql2
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: bundler
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: test-unit
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - ">="
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - ">="
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '0'
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: rake
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
|
-
- -
|
59
|
+
- - ">="
|
68
60
|
- !ruby/object:Gem::Version
|
69
61
|
version: '0'
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
|
-
- -
|
66
|
+
- - ">="
|
76
67
|
- !ruby/object:Gem::Version
|
77
68
|
version: '0'
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: shoulda-context
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
|
-
- -
|
73
|
+
- - ">="
|
84
74
|
- !ruby/object:Gem::Version
|
85
75
|
version: '0'
|
86
76
|
type: :development
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
|
-
- -
|
80
|
+
- - ">="
|
92
81
|
- !ruby/object:Gem::Version
|
93
82
|
version: '0'
|
94
83
|
- !ruby/object:Gem::Dependency
|
95
84
|
name: mocha
|
96
85
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
86
|
requirements:
|
99
|
-
- -
|
87
|
+
- - ">="
|
100
88
|
- !ruby/object:Gem::Version
|
101
89
|
version: 0.13.3
|
102
90
|
type: :development
|
103
91
|
prerelease: false
|
104
92
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
93
|
requirements:
|
107
|
-
- -
|
94
|
+
- - ">="
|
108
95
|
- !ruby/object:Gem::Version
|
109
96
|
version: 0.13.3
|
110
97
|
description: Distributed storage
|
@@ -117,7 +104,7 @@ executables:
|
|
117
104
|
extensions: []
|
118
105
|
extra_rdoc_files: []
|
119
106
|
files:
|
120
|
-
- .gitignore
|
107
|
+
- ".gitignore"
|
121
108
|
- Gemfile
|
122
109
|
- LICENSE
|
123
110
|
- README.md
|
@@ -148,6 +135,7 @@ files:
|
|
148
135
|
- lib/filecluster.rb
|
149
136
|
- lib/manage.rb
|
150
137
|
- lib/manage/copy_rules.rb
|
138
|
+
- lib/manage/copy_speed.rb
|
151
139
|
- lib/manage/item.rb
|
152
140
|
- lib/manage/policies.rb
|
153
141
|
- lib/manage/show.rb
|
@@ -169,33 +157,26 @@ files:
|
|
169
157
|
- test/version_test.rb
|
170
158
|
homepage: ''
|
171
159
|
licenses: []
|
160
|
+
metadata: {}
|
172
161
|
post_install_message:
|
173
162
|
rdoc_options: []
|
174
163
|
require_paths:
|
175
164
|
- lib
|
176
165
|
required_ruby_version: !ruby/object:Gem::Requirement
|
177
|
-
none: false
|
178
166
|
requirements:
|
179
|
-
- -
|
167
|
+
- - ">="
|
180
168
|
- !ruby/object:Gem::Version
|
181
169
|
version: '0'
|
182
|
-
segments:
|
183
|
-
- 0
|
184
|
-
hash: -4597744036765809736
|
185
170
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
186
|
-
none: false
|
187
171
|
requirements:
|
188
|
-
- -
|
172
|
+
- - ">="
|
189
173
|
- !ruby/object:Gem::Version
|
190
174
|
version: '0'
|
191
|
-
segments:
|
192
|
-
- 0
|
193
|
-
hash: -4597744036765809736
|
194
175
|
requirements: []
|
195
176
|
rubyforge_project:
|
196
|
-
rubygems_version:
|
177
|
+
rubygems_version: 2.4.3
|
197
178
|
signing_key:
|
198
|
-
specification_version:
|
179
|
+
specification_version: 4
|
199
180
|
summary: Distributed storage
|
200
181
|
test_files:
|
201
182
|
- test/base_test.rb
|