filecluster 0.1.7 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +13 -0
- data/TODO +1 -1
- data/bin/fc-daemon +5 -7
- data/bin/fc-manage +18 -26
- data/lib/daemon.rb +19 -11
- data/lib/daemon/global_daemon_thread.rb +24 -8
- data/lib/daemon/task_thread.rb +1 -2
- data/lib/fc/db.rb +13 -1
- data/lib/fc/item.rb +1 -1
- data/lib/fc/var.rb +34 -0
- data/lib/fc/version.rb +1 -1
- data/lib/filecluster.rb +1 -0
- data/lib/manage.rb +1 -0
- data/lib/manage/policies.rb +2 -2
- data/lib/manage/show.rb +1 -1
- data/lib/manage/storages.rb +77 -0
- data/lib/manage/var.rb +51 -0
- data/test/daemon_test.rb +19 -3
- data/test/storage_sync.rb +65 -0
- data/test/var_test.rb +33 -0
- metadata +27 -43
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b456dec7949f50fd5082b13bd5aad2c6ecf852c6
|
4
|
+
data.tar.gz: 0c05bff392e9a9fddb9843104cef69335327fd1e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cdd07e299a989e265c94045198104600cf6a87234cea244ca8d32bc4774a2c5820583d21670d2a396da8792f678e573d772a14607e5d19b600e1f516ce3d180a
|
7
|
+
data.tar.gz: cf967fd389881cf282da7a93516710c5998ef4df4fa4790f915781d2134c57ebd2dcdfd41b2e5d52f884748be118ff998baca0960bca1b84e9dffb5617b67188
|
data/README.md
CHANGED
@@ -33,6 +33,19 @@ Selecting available storage to store item by policy.create_storages (from left t
|
|
33
33
|
|
34
34
|
Selecting available storage to copy item by policy.copy_storages (from left to tight) with the nearest copy_id.
|
35
35
|
|
36
|
+
## Variables
|
37
|
+
|
38
|
+
|name|default value|description|
|
39
|
+
|:---|:-----------:|:---------|
|
40
|
+
|global_daemon_host||set fc_daemon when run global daemon task (only in one instance)|
|
41
|
+
|daemon_cycle_time|30|time between global daemon checks and storages available checks|
|
42
|
+
|daemon_global_wait_time|120|time between runs global daemon if it does not running|
|
43
|
+
|daemon_global_tasks_group_limit|1000|limit for select for tasks|
|
44
|
+
|daemon_global_error_items_ttl|86400|ttl for items with error status before delete|
|
45
|
+
|daemon_global_error_items_storages_ttl|86400|ttl for items_storages with error status before delete|
|
46
|
+
|daemon_global_tasks_per_thread|10|tasks count for one task thread|
|
47
|
+
|daemon_global_tasks_threads_limit|3|tasks threads count limit for one storage|
|
48
|
+
|
36
49
|
## Usage
|
37
50
|
|
38
51
|
TODO: Write usage instructions here
|
data/TODO
CHANGED
data/bin/fc-daemon
CHANGED
@@ -18,10 +18,8 @@ $global_daemon_thread = nil
|
|
18
18
|
|
19
19
|
default_db_config = File.expand_path(File.dirname(__FILE__))+'/db.yml'
|
20
20
|
descriptions = {
|
21
|
-
:config => {:short => 'c', :full => 'config',
|
22
|
-
:log_level => {:short => 'l', :full => 'log_level',
|
23
|
-
:cycle_time => {:short => 't', :full => 'time', :default => 30, :text => 'Time between checks database and storages available, default 30'},
|
24
|
-
:global_wait => {:short => 'g', :full => 'wait', :default => 120, :text => 'Time between runs global daemon if it does not running, default 120'},
|
21
|
+
:config => {:short => 'c', :full => 'config', :default => default_db_config, :text => "path to db.yml file, default #{default_db_config}"},
|
22
|
+
:log_level => {:short => 'l', :full => 'log_level', :default => 'info', :text => 'log level (fatal, error, warn, info or debug), default info'},
|
25
23
|
:curr_host => {:short => 'h', :full => 'host', :default => FC::Storage.curr_host, :text => "Host for storages, default #{FC::Storage.curr_host}"}
|
26
24
|
}
|
27
25
|
desc = %q{Run FileCluster daemon.
|
@@ -42,7 +40,7 @@ FC::DB.connect_by_config(db_options.merge(:reconnect => true, :multi_threads =>
|
|
42
40
|
$log.info('Connected to database')
|
43
41
|
|
44
42
|
def quit_on_quit
|
45
|
-
$log.info('Exit signal')
|
43
|
+
Thread.new { $log.info('Exit signal') }.join
|
46
44
|
$exit_signal = true
|
47
45
|
end
|
48
46
|
trap("TERM") {quit_on_quit}
|
@@ -59,12 +57,12 @@ while true do
|
|
59
57
|
$log.info('Exit')
|
60
58
|
exit
|
61
59
|
else
|
62
|
-
run_global_daemon
|
60
|
+
run_global_daemon
|
63
61
|
update_storages
|
64
62
|
storages_check
|
65
63
|
update_tasks
|
66
64
|
run_tasks
|
67
65
|
end
|
68
66
|
$log.debug('sleep')
|
69
|
-
sleep
|
67
|
+
sleep FC::Var.get('daemon_cycle_time', 30).to_i
|
70
68
|
end
|
data/bin/fc-manage
CHANGED
@@ -24,6 +24,8 @@ Command:
|
|
24
24
|
rm <name> delete storage
|
25
25
|
change <name> change storage attributes
|
26
26
|
update_size <name> update storage size
|
27
|
+
sync_info show information about synchronization storage and file system
|
28
|
+
sync synchronize storage and file system
|
27
29
|
}],
|
28
30
|
'policies' => [
|
29
31
|
'show list and manage plicies',
|
@@ -37,13 +39,21 @@ Command:
|
|
37
39
|
}],
|
38
40
|
'show' => [
|
39
41
|
'show variable',
|
40
|
-
%q{Usage: fc-manage [options] show <
|
41
|
-
|
42
|
+
%q{Usage: fc-manage [options] show <command>
|
43
|
+
Command:
|
42
44
|
current_host show current host
|
43
45
|
global_daemon show host and uptime where run global daemon
|
44
46
|
errors [<count>] show last count (default 10) errors
|
45
47
|
host_info [<host>] show info for host (default current host)
|
46
48
|
items_info show items statistics
|
49
|
+
}],
|
50
|
+
'var' => [
|
51
|
+
'show and change FC::Var',
|
52
|
+
%q{Usage: fc-manage [options] var <command>
|
53
|
+
Command:
|
54
|
+
list show all FC::Var-s
|
55
|
+
show <variable> show current value for variable
|
56
|
+
change <variable> change variable
|
47
57
|
}]
|
48
58
|
}
|
49
59
|
desc = %q{Get info and manage for storages, policies and items.
|
@@ -66,8 +76,7 @@ if ARGV.empty?
|
|
66
76
|
exit
|
67
77
|
end
|
68
78
|
|
69
|
-
|
70
|
-
when 'help'
|
79
|
+
if command == 'help'
|
71
80
|
if !ARGV[1]
|
72
81
|
puts $options['optparse']
|
73
82
|
elsif commands_help[ARGV[1]]
|
@@ -75,31 +84,14 @@ when 'help'
|
|
75
84
|
else
|
76
85
|
puts "'#{command}' is not a fc-manage command. See 'fc-manage --help'."
|
77
86
|
end
|
78
|
-
|
79
|
-
if !ARGV[1]
|
80
|
-
puts "Need variable name. See 'fc-manage help show'."
|
81
|
-
elsif self.private_methods.member?("show_#{ARGV[1]}".to_sym)
|
82
|
-
send "show_#{ARGV[1]}"
|
83
|
-
else
|
84
|
-
puts "Unknown variable. See 'fc-manage help show'."
|
85
|
-
end
|
86
|
-
when 'storages'
|
87
|
+
elsif commands_help[command]
|
87
88
|
if !ARGV[1]
|
88
89
|
puts "Need command. See 'fc-manage help storages'."
|
89
|
-
elsif self.private_methods.member?("
|
90
|
-
send "
|
90
|
+
elsif self.private_methods.member?("#{command}_#{ARGV[1]}".to_sym)
|
91
|
+
send "#{command}_#{ARGV[1]}"
|
91
92
|
else
|
92
|
-
puts "Unknown command. See 'fc-manage help
|
93
|
-
end
|
94
|
-
when 'policies'
|
95
|
-
if !ARGV[1]
|
96
|
-
puts "Need command. See 'fc-manage help policies'."
|
97
|
-
elsif self.private_methods.member?("policies_#{ARGV[1]}".to_sym)
|
98
|
-
send "policies_#{ARGV[1]}"
|
99
|
-
else
|
100
|
-
puts "Unknown command. See 'fc-manage help policies'."
|
93
|
+
puts "Unknown command. See 'fc-manage help #{command}'."
|
101
94
|
end
|
102
95
|
else
|
103
96
|
puts "'#{command}' is not a fc-manage command. See 'fc-manage --help'."
|
104
|
-
end
|
105
|
-
|
97
|
+
end
|
data/lib/daemon.rb
CHANGED
@@ -15,12 +15,13 @@ class << FC::Error
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def run_global_daemon
|
18
|
+
def run_global_daemon
|
19
19
|
$log.debug('Run global daemon check')
|
20
|
+
timeout = FC::Var.get('daemon_global_wait_time', 120).to_i
|
20
21
|
r = FC::DB.query("SELECT #{FC::DB.prefix}vars.*, UNIX_TIMESTAMP() as curr_time FROM #{FC::DB.prefix}vars WHERE name='global_daemon_host'").first
|
21
22
|
if !r || r['curr_time'].to_i - r['time'].to_i > timeout
|
22
23
|
$log.debug('Set global daemon host to current')
|
23
|
-
FC::
|
24
|
+
FC::Var.set('global_daemon_host', FC::Storage.curr_host)
|
24
25
|
sleep 1
|
25
26
|
r = FC::DB.query("SELECT #{FC::DB.prefix}vars.*, UNIX_TIMESTAMP() as curr_time FROM #{FC::DB.prefix}vars WHERE name='global_daemon_host'").first
|
26
27
|
end
|
@@ -66,14 +67,17 @@ def update_tasks
|
|
66
67
|
cond = "storage_name in (#{storages_names}) AND status='#{type.to_s}'"
|
67
68
|
ids = $tasks.map{|storage_name, storage_tasks| storage_tasks.select{|task| task[:action] == type}}.
|
68
69
|
flatten.map{|task| task[:item_storage].id}
|
69
|
-
ids += $curr_tasks.select{|task| task[:action] == type}
|
70
|
-
|
70
|
+
ids += $curr_tasks.select{|task| task[:action] == type}.map{|task| task[:item_storage].id}
|
71
|
+
|
72
|
+
limit = FC::Var.get('daemon_global_tasks_group_limit', 1000).to_i
|
71
73
|
cond << "AND id not in (#{ids.join(',')})" if (ids.length > 0)
|
72
|
-
cond << "LIMIT
|
74
|
+
cond << " LIMIT #{limit}"
|
73
75
|
FC::ItemStorage.where(cond).each do |item_storage|
|
74
|
-
|
75
|
-
|
76
|
-
|
76
|
+
unless ids.include?(item_storage.id)
|
77
|
+
$tasks[item_storage.storage_name] = [] unless $tasks[item_storage.storage_name]
|
78
|
+
$tasks[item_storage.storage_name] << {:action => type, :item_storage => item_storage}
|
79
|
+
$log.debug("task add: type=#{type}, item_storage=#{item_storage.id}")
|
80
|
+
end
|
77
81
|
end
|
78
82
|
end
|
79
83
|
|
@@ -88,9 +92,13 @@ def run_tasks
|
|
88
92
|
$tasks_threads[storage.name].delete_if {|thread| !thread.alive?}
|
89
93
|
tasks_count = $tasks[storage.name] ? $tasks[storage.name].size : 0
|
90
94
|
threads_count = $tasks_threads[storage.name].count
|
91
|
-
|
92
|
-
|
93
|
-
|
95
|
+
|
96
|
+
# <max_threads> tasks per thread, maximum <tasks_per_thread> threads
|
97
|
+
max_threads = FC::Var.get('daemon_global_tasks_threads_limit', 3).to_i
|
98
|
+
tasks_per_thread = FC::Var.get('daemon_global_tasks_per_thread', 10).to_i
|
99
|
+
|
100
|
+
run_threads_count = (tasks_count/tasks_per_thread.to_f).ceil - threads_count
|
101
|
+
run_threads_count = max_threads if run_threads_count > max_threads
|
94
102
|
$log.debug("tasks_count: #{tasks_count}, threads_count: #{threads_count}, run_threads_count: #{run_threads_count}")
|
95
103
|
run_threads_count.times do
|
96
104
|
$log.debug("spawn TaskThread for #{storage.name}")
|
@@ -6,18 +6,18 @@ class GlobalDaemonThread < BaseThread
|
|
6
6
|
sleep timeout.to_f/2
|
7
7
|
exit if $exit_signal
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
FC::DB.query("UPDATE #{FC::DB.prefix}vars SET val='#{FC::Storage.curr_host}' WHERE name='global_daemon_host'")
|
9
|
+
if FC::Var.get('global_daemon_host') == FC::Storage.curr_host
|
10
|
+
FC::Var.set('global_daemon_host', FC::Storage.curr_host)
|
12
11
|
else
|
13
|
-
$log.info("Exit from GlobalDaemonThread: global daemon already running on #{
|
12
|
+
$log.info("Exit from GlobalDaemonThread: global daemon already running on #{FC::Var.get('global_daemon_host')}")
|
14
13
|
FC::DB.close
|
15
14
|
exit
|
16
15
|
end
|
17
16
|
|
18
17
|
make_item_copies
|
18
|
+
make_deleted_error_items_storages
|
19
|
+
make_deleted_error_items
|
19
20
|
delete_deleted_items
|
20
|
-
#периодическая проверка на item со статусом delete, последним обновлением дольше суток (NOW - time > 86400) и без связанных is - удаление таких из базы
|
21
21
|
#TODO: периодически удалять (проставлять статус delete) для лиших is (число копий больше необходимого)
|
22
22
|
end
|
23
23
|
end
|
@@ -37,9 +37,10 @@ class GlobalDaemonThread < BaseThread
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
+
limit = FC::Var.get('daemon_global_tasks_group_limit', 1000).to_i
|
40
41
|
sql = "SELECT i.id as item_id, i.size, i.copies as item_copies, GROUP_CONCAT(ist.storage_name ORDER BY ist.id) as storages, p.id as policy_id, p.copies as policy_copies "+
|
41
42
|
"FROM #{FC::Item.table_name} as i, #{FC::Policy.table_name} as p, #{FC::ItemStorage.table_name} as ist WHERE "+
|
42
|
-
"i.policy_id = p.id AND ist.item_id = i.id AND i.copies > 0 AND i.copies < p.copies AND i.status = 'ready' AND ist.status <> 'delete' GROUP BY i.id LIMIT
|
43
|
+
"i.policy_id = p.id AND ist.item_id = i.id AND i.copies > 0 AND i.copies < p.copies AND i.status = 'ready' AND ist.status <> 'delete' GROUP BY i.id LIMIT #{limit}"
|
43
44
|
r = FC::DB.query(sql)
|
44
45
|
r.each do |row|
|
45
46
|
$log.info("GlobalDaemonThread: new item_storage for item #{row['item_id']}")
|
@@ -52,8 +53,11 @@ class GlobalDaemonThread < BaseThread
|
|
52
53
|
src_storage = all_storages.detect{|s| item_storages.first == s.name}
|
53
54
|
policy = all_policies.detect{|p| row['policy_id'] == p.id}
|
54
55
|
storage = policy.get_proper_storage_for_copy(row['size'], src_storage.copy_id, item_storages) if src_storage && policy
|
55
|
-
|
56
|
-
|
56
|
+
if storage
|
57
|
+
FC::Item.new(:id => row['item_id']).make_item_storage(storage, 'copy')
|
58
|
+
else
|
59
|
+
error 'No available storage', :item_id => row['item_id']
|
60
|
+
end
|
57
61
|
end
|
58
62
|
end
|
59
63
|
end
|
@@ -69,4 +73,16 @@ class GlobalDaemonThread < BaseThread
|
|
69
73
|
$log.info("GlobalDaemonThread: delete items #{ids}")
|
70
74
|
end
|
71
75
|
end
|
76
|
+
|
77
|
+
def make_deleted_error_items_storages
|
78
|
+
$log.debug("GlobalDaemonThread: make_deleted_error_items_storages")
|
79
|
+
ttl = FC::Var.get('daemon_global_error_items_storages_ttl', 86400).to_i
|
80
|
+
FC::DB.query("UPDATE #{FC::ItemStorage.table_name} SET status = 'delete' WHERE status = 'error' AND time < #{Time.new.to_i - ttl}")
|
81
|
+
end
|
82
|
+
|
83
|
+
def make_deleted_error_items
|
84
|
+
$log.debug("GlobalDaemonThread: make_deleted_error_items")
|
85
|
+
ttl = FC::Var.get('daemon_global_error_items_ttl', 86400).to_i
|
86
|
+
FC::DB.query("UPDATE #{FC::Item.table_name} SET status = 'delete' WHERE status = 'error' AND time < #{Time.new.to_i - ttl}")
|
87
|
+
end
|
72
88
|
end
|
data/lib/daemon/task_thread.rb
CHANGED
@@ -18,7 +18,6 @@ class TaskThread < BaseThread
|
|
18
18
|
|
19
19
|
def make_delete(task)
|
20
20
|
item_storage = task[:item_storage]
|
21
|
-
# TODO: не лазить в базу за item
|
22
21
|
storage = $storages.detect{|s| s.name == item_storage.storage_name}
|
23
22
|
item = FC::Item.find(item_storage.item_id)
|
24
23
|
storage.delete_file(item.name)
|
@@ -30,9 +29,9 @@ class TaskThread < BaseThread
|
|
30
29
|
|
31
30
|
def make_copy(task)
|
32
31
|
item_storage = task[:item_storage]
|
33
|
-
# TODO: не лазить в базу за item, item_storages - перенести на стадию подготовки task-а
|
34
32
|
storage = $storages.detect{|s| s.name == item_storage.storage_name}
|
35
33
|
item = FC::Item.find(item_storage.item_id)
|
34
|
+
return nil unless item && item.status == 'ready'
|
36
35
|
src_item_storage = FC::ItemStorage.where("item_id = ? AND status = 'ready'", item.id).sample
|
37
36
|
unless src_item_storage
|
38
37
|
$log.info("Item ##{item.id} #{item.name} has no ready item_storage")
|
data/lib/fc/db.rb
CHANGED
@@ -48,7 +48,11 @@ module FC
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
def
|
51
|
+
def self.server_time
|
52
|
+
FC::DB.query("SELECT UNIX_TIMESTAMP() as curr_time").first['curr_time'].to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.init_db
|
52
56
|
FC::DB.connect.query(%{
|
53
57
|
CREATE TABLE #{@prefix}items (
|
54
58
|
id int NOT NULL AUTO_INCREMENT,
|
@@ -185,12 +189,20 @@ module FC
|
|
185
189
|
CREATE TABLE #{@prefix}vars (
|
186
190
|
name varchar(255) DEFAULT NULL,
|
187
191
|
val varchar(255) DEFAULT NULL,
|
192
|
+
descr text DEFAULT NULL,
|
188
193
|
time int DEFAULT NULL,
|
189
194
|
PRIMARY KEY (name)
|
190
195
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
|
191
196
|
})
|
192
197
|
FC::DB.connect.query("CREATE TRIGGER fc_vars_before_insert BEFORE INSERT on #{@prefix}vars FOR EACH ROW BEGIN #{proc_time} END")
|
193
198
|
FC::DB.connect.query("CREATE TRIGGER fc_vars_before_update BEFORE UPDATE on #{@prefix}vars FOR EACH ROW BEGIN #{proc_time} END")
|
199
|
+
FC::DB.connect.query("INSERT INTO #{@prefix}vars SET name='daemon_cycle_time', val='30', descr='time between global daemon checks and storages available checks'")
|
200
|
+
FC::DB.connect.query("INSERT INTO #{@prefix}vars SET name='daemon_global_wait_time', val='120', descr='time between runs global daemon if it does not running'")
|
201
|
+
FC::DB.connect.query("INSERT INTO #{@prefix}vars SET name='daemon_global_tasks_group_limit', val='1000', descr='limit for select for tasks'")
|
202
|
+
FC::DB.connect.query("INSERT INTO #{@prefix}vars SET name='daemon_global_error_items_ttl', val='86400', descr='ttl for items with error status before delete'")
|
203
|
+
FC::DB.connect.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'")
|
204
|
+
FC::DB.connect.query("INSERT INTO #{@prefix}vars SET name='daemon_global_tasks_per_thread', val='10', descr='tasks count for one task thread'")
|
205
|
+
FC::DB.connect.query("INSERT INTO #{@prefix}vars SET name='daemon_global_tasks_threads_limit', val='3', descr='tasks threads count limit for one storage'")
|
194
206
|
end
|
195
207
|
end
|
196
208
|
end
|
data/lib/fc/item.rb
CHANGED
@@ -7,7 +7,7 @@ module FC
|
|
7
7
|
# Create item by local path.
|
8
8
|
# Additional options:
|
9
9
|
# :replace=true - replace item if it exists
|
10
|
-
# If item_name is part of local_path it
|
10
|
+
# If item_name is part of local_path it processed as inplace - local_path is valid path to the item for policy
|
11
11
|
def self.create_from_local(local_path, item_name, policy, options={})
|
12
12
|
raise 'Path not exists' unless File.exists?(local_path)
|
13
13
|
raise 'Policy is not FC::Policy' unless policy.instance_of?(FC::Policy)
|
data/lib/fc/var.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FC
|
4
|
+
class Var
|
5
|
+
class << self
|
6
|
+
attr_accessor :cache_time
|
7
|
+
end
|
8
|
+
@cache_time = 120 # ttl for get_all
|
9
|
+
@all_vars = {}
|
10
|
+
|
11
|
+
def self.set(name, val)
|
12
|
+
FC::DB.query("UPDATE #{FC::DB.prefix}vars SET val='#{Mysql2::Client.escape(val.to_s)}' WHERE name='#{Mysql2::Client.escape(name.to_s)}'")
|
13
|
+
FC::DB.query("INSERT IGNORE INTO #{FC::DB.prefix}vars SET val='#{Mysql2::Client.escape(val.to_s)}', name='#{Mysql2::Client.escape(name.to_s)}'")
|
14
|
+
@all_vars[name.to_s] = val.to_s
|
15
|
+
@all_vars[name.to_sym] = val.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.get(name, default_value = nil)
|
19
|
+
get_all[name] || default_value
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.get_all
|
23
|
+
if !@get_all_read_time || Time.new.to_i - @get_all_read_time > cache_time
|
24
|
+
@all_vars = {}
|
25
|
+
FC::DB.query("SELECT * FROM #{FC::DB.prefix}vars").each do |row|
|
26
|
+
@all_vars[row['name']] = row['val']
|
27
|
+
@all_vars[row['name'].to_sym] = row['val']
|
28
|
+
end
|
29
|
+
@get_all_read_time = Time.new.to_i
|
30
|
+
end
|
31
|
+
@all_vars
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/fc/version.rb
CHANGED
data/lib/filecluster.rb
CHANGED
data/lib/manage.rb
CHANGED
data/lib/manage/policies.rb
CHANGED
@@ -85,8 +85,8 @@ def policies_change
|
|
85
85
|
copy_storages = copy_storages.split(',').select{|s| storages.member?(s.strip)}.join(',').strip unless copy_storages.empty?
|
86
86
|
|
87
87
|
policy.name = name unless name.empty?
|
88
|
-
policy.create_storages =
|
89
|
-
policy.copy_storages =
|
88
|
+
policy.create_storages = create_storages unless create_storages.empty?
|
89
|
+
policy.copy_storages = copy_storages unless copy_storages.empty?
|
90
90
|
policy.copies = copies.to_i unless copies.empty?
|
91
91
|
|
92
92
|
puts %Q{\nStorage
|
data/lib/manage/show.rb
CHANGED
@@ -4,7 +4,7 @@ end
|
|
4
4
|
|
5
5
|
def show_global_daemon
|
6
6
|
r = FC::DB.query("SELECT #{FC::DB.prefix}vars.*, UNIX_TIMESTAMP() as curr_time FROM #{FC::DB.prefix}vars WHERE name='global_daemon_host'").first
|
7
|
-
if r
|
7
|
+
if r['val']
|
8
8
|
puts "Global daemon run on #{r['val']}\nLast run #{r['curr_time']-r['time']} seconds ago."
|
9
9
|
else
|
10
10
|
puts "Global daemon is not runnning."
|
data/lib/manage/storages.rb
CHANGED
@@ -146,6 +146,28 @@ def storages_change
|
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
149
|
+
def sync_info
|
150
|
+
if storage = find_storage
|
151
|
+
print "Synchronization info for (#{storage.name}) storage and file system (#{storage.path})."
|
152
|
+
make_storages_sync(storage, false)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def storages_sync
|
157
|
+
if storage = find_storage
|
158
|
+
print "Synchronize (#{storage.name}) storage and file system (#{storage.path})."
|
159
|
+
s = Readline.readline("Continue? (y/n) ", false).strip.downcase
|
160
|
+
puts ""
|
161
|
+
if s == "y" || s == "yes"
|
162
|
+
make_storages_sync(storage, true)
|
163
|
+
puts "Synchronize done."
|
164
|
+
storages_update_size
|
165
|
+
else
|
166
|
+
puts "Canceled."
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
149
171
|
private
|
150
172
|
|
151
173
|
def find_storage
|
@@ -154,3 +176,58 @@ def find_storage
|
|
154
176
|
puts "Storage #{name} not found." if !storage
|
155
177
|
storage
|
156
178
|
end
|
179
|
+
|
180
|
+
def make_storages_sync(storage, make_delete, silent = false)
|
181
|
+
# get all items for storage
|
182
|
+
db_items = {}
|
183
|
+
FC::DB.query("SELECT i.name, ist.id FROM #{FC::Item.table_name} as i, #{FC::ItemStorage.table_name} as ist WHERE ist.item_id = i.id AND ist.storage_name = '#{storage.name}'").each do |row|
|
184
|
+
name = row['name'].sub(/\/$/, '').sub(/^\//, '').strip
|
185
|
+
path = ''
|
186
|
+
name.split('/').each do |dir|
|
187
|
+
path << dir
|
188
|
+
db_items[path] = [false, path == name ? row['id'].to_i : nil]
|
189
|
+
path << '/'
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# walk on all storage folders and files
|
194
|
+
delete_files = []
|
195
|
+
process_storage_dir_sync = lambda do |dir = ''|
|
196
|
+
Dir.glob(storage.path+dir+'*').each do |f|
|
197
|
+
path = f.sub(storage.path, '')
|
198
|
+
if db_items[path]
|
199
|
+
db_items[path][0] = true
|
200
|
+
next if db_items[path][1]
|
201
|
+
end
|
202
|
+
delete_files << path if File.file?(f)
|
203
|
+
process_storage_dir_sync.call(path+'/') if File.directory?(f)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
process_storage_dir_sync.call
|
207
|
+
|
208
|
+
# rm delete_files
|
209
|
+
if make_delete
|
210
|
+
delete_files.each do |f|
|
211
|
+
# check in DB again
|
212
|
+
next if FC::DB.query("SELECT ist.id FROM #{FC::Item.table_name} as i, #{FC::ItemStorage.table_name} as ist WHERE ist.item_id = i.id AND ist.storage_name = '#{storage.name}' AND i.name='#{f}'").first
|
213
|
+
path = storage.path+f
|
214
|
+
File.delete(path) rescue nil
|
215
|
+
end
|
216
|
+
end
|
217
|
+
puts "Deleted #{delete_files.count} files" unless silent
|
218
|
+
|
219
|
+
# delete non synchronize items_storages
|
220
|
+
count = 0
|
221
|
+
db_items.values.each do |item|
|
222
|
+
if !item[0] && item[1]
|
223
|
+
count += 1
|
224
|
+
FC::DB.query("DELETE FROM #{FC::ItemStorage.table_name} WHERE id=#{item[1]}") if make_delete
|
225
|
+
end
|
226
|
+
end
|
227
|
+
puts "Deleted #{count} items_storages" unless silent
|
228
|
+
|
229
|
+
# delete empty folders
|
230
|
+
count = `find #{storage.path} -empty -type d`.split("\n").count
|
231
|
+
`find #{storage.path} -empty -type d -delete` if make_delete
|
232
|
+
puts "Deleted #{count} empty folders" unless silent
|
233
|
+
end
|
data/lib/manage/var.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
def var_list
|
2
|
+
vars = FC::DB.query("SELECT * FROM #{FC::DB.prefix}vars WHERE descr IS NOT NULL")
|
3
|
+
if vars.size == 0
|
4
|
+
puts "No vars."
|
5
|
+
else
|
6
|
+
vars.each do |var|
|
7
|
+
puts var['name']
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def var_show
|
13
|
+
if var = find_var
|
14
|
+
puts %Q{Var
|
15
|
+
Name: #{var['name']}
|
16
|
+
Value: #{var['val']}
|
17
|
+
Description: #{var['descr']}}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def var_change
|
22
|
+
if var = find_var
|
23
|
+
puts "Change var #{var['name']}"
|
24
|
+
val = stdin_read_val("Value (now #{var['val']})")
|
25
|
+
|
26
|
+
puts %Q{\nVar
|
27
|
+
Name: #{var['name']}
|
28
|
+
Value: #{val}}
|
29
|
+
s = Readline.readline("Continue? (y/n) ", false).strip.downcase
|
30
|
+
puts ""
|
31
|
+
if s == "y" || s == "yes"
|
32
|
+
begin
|
33
|
+
FC::Var.set(var['name'], val)
|
34
|
+
rescue Exception => e
|
35
|
+
puts "Error: #{e.message}"
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
puts "ok"
|
39
|
+
else
|
40
|
+
puts "Canceled."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def find_var
|
48
|
+
var = FC::DB.query("SELECT * FROM #{FC::DB.prefix}vars WHERE descr IS NOT NULL AND name='#{Mysql2::Client.escape(ARGV[2].to_s)}'").first
|
49
|
+
puts "Var #{ARGV[2]} not found." unless var
|
50
|
+
var
|
51
|
+
end
|
data/test/daemon_test.rb
CHANGED
@@ -17,9 +17,13 @@ class DaemonTest < Test::Unit::TestCase
|
|
17
17
|
|
18
18
|
@@errors_count = FC::Error.where.count
|
19
19
|
|
20
|
+
FC::Var.set('daemon_cycle_time', 1)
|
21
|
+
FC::Var.set('daemon_global_wait_time', 1)
|
22
|
+
FC::Var.set('daemon_global_error_items_storages_ttl', 0)
|
23
|
+
FC::Var.set('daemon_global_error_items_ttl', 0)
|
20
24
|
@stotage_checks = 0
|
21
25
|
Thread.new do
|
22
|
-
Open3.popen2e("#{daemon_bin} -c #{db_config_file} -l debug -
|
26
|
+
Open3.popen2e("#{daemon_bin} -c #{db_config_file} -l debug -h host1") do |stdin, stdout, t|
|
23
27
|
@@pid = t.pid
|
24
28
|
while line = stdout.readline
|
25
29
|
@stotage_checks += 1 if line =~ /Finish stotage check/i
|
@@ -90,7 +94,8 @@ class DaemonTest < Test::Unit::TestCase
|
|
90
94
|
assert_equal `du -sb /tmp/host1-sda/bla/bla/test$i 2>&1`.to_i, `du -sb /tmp/host$i-sd$j/bla/bla/test$i 2>&1`.to_i
|
91
95
|
end
|
92
96
|
end
|
93
|
-
|
97
|
+
assert_equal @@errors_count, FC::Error.where.count, "new errors in errors table"
|
98
|
+
|
94
99
|
@@policy.copies = 2
|
95
100
|
@@policy.save
|
96
101
|
item_storage = FC::ItemStorage.where('item_id = ? AND storage_name = ?', @item1.id, 'host1-sdc').first
|
@@ -101,7 +106,18 @@ class DaemonTest < Test::Unit::TestCase
|
|
101
106
|
assert_equal @@errors_count, FC::Error.where.count, "new errors in errors table"
|
102
107
|
|
103
108
|
@item1.mark_deleted
|
109
|
+
FC::ItemStorage.where('item_id = ?', @item2.id).each do |item_storage|
|
110
|
+
item_storage.status = 'error'
|
111
|
+
item_storage.save
|
112
|
+
end
|
113
|
+
@item3.status = 'error'
|
114
|
+
@item3.save
|
115
|
+
|
104
116
|
sleep 2
|
105
|
-
assert_raise(RuntimeError,
|
117
|
+
assert_raise(RuntimeError, "Item not deleted after mark_deleted") {@item1.reload}
|
118
|
+
assert_equal 0, FC::ItemStorage.where('item_id = ?', @item2.id).count, "ItemStorages not deleted after status='error'"
|
119
|
+
@item3.reload
|
120
|
+
assert_equal 'delete', @item3.status, "ItemStorages not deleted after status='error'"
|
121
|
+
assert_equal @@errors_count, FC::Error.where.count, "new errors in errors table"
|
106
122
|
end
|
107
123
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'manage'
|
3
|
+
|
4
|
+
class DaemonTest < Test::Unit::TestCase
|
5
|
+
class << self
|
6
|
+
def startup
|
7
|
+
# tmp fake storage dir
|
8
|
+
`rm -rf /tmp/host*-sd*`
|
9
|
+
`mkdir -p /tmp/host1-sda/`
|
10
|
+
|
11
|
+
# test files to copy
|
12
|
+
@@test_file_path = '/tmp/fc_test_file'
|
13
|
+
`dd if=/dev/urandom of=#{@@test_file_path} bs=1M count=1 2>&1`
|
14
|
+
|
15
|
+
@@storage = FC::Storage.new(:name => 'host1-sda', :host => 'host1', :path => '/tmp/host1-sda/', :copy_id => 1, :size_limit => 1000000000, :check_time => Time.new.to_i)
|
16
|
+
@@storage.save
|
17
|
+
@@policy = FC::Policy.new(:create_storages => 'host1-sda', :copy_storages => 'host1-sda', :copies => 1, :name => 'policy 1')
|
18
|
+
@@policy.save
|
19
|
+
end
|
20
|
+
|
21
|
+
def shutdown
|
22
|
+
FC::DB.query("DELETE FROM items_storages")
|
23
|
+
FC::DB.query("DELETE FROM items")
|
24
|
+
FC::DB.query("DELETE FROM policies")
|
25
|
+
FC::DB.query("DELETE FROM storages")
|
26
|
+
`rm -rf /tmp/host*-sd*`
|
27
|
+
`rm -rf #{@@test_file_path}`
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup
|
32
|
+
FC::Storage.any_instance.stubs(:host).returns('host1')
|
33
|
+
FC::Storage.stubs(:curr_host).returns('host1')
|
34
|
+
end
|
35
|
+
|
36
|
+
should "sync_all" do
|
37
|
+
assert_nothing_raised { @item1 = FC::Item.create_from_local(@@test_file_path, 'a/test1', @@policy, {:tag => 'test'}) }
|
38
|
+
assert_nothing_raised { @item2 = FC::Item.create_from_local(@@test_file_path, 'a/b/test2', @@policy, {:tag => 'test'}) }
|
39
|
+
assert_nothing_raised { @item3 = FC::Item.create_from_local(@@test_file_path, 'a/b/c/test3', @@policy, {:tag => 'test'}) }
|
40
|
+
assert_nothing_raised { @item4 = FC::Item.create_from_local(@@test_file_path, 'a/b/c/d/test4', @@policy, {:tag => 'test'}) }
|
41
|
+
`mv /tmp/host1-sda/a/test1 /tmp/host1-sda/test1`
|
42
|
+
`mv /tmp/host1-sda/a/b/c/d/test4 /tmp/host1-sda/a/b/c/d/test5`
|
43
|
+
`mkdir /tmp/host1-sda/test_dir`
|
44
|
+
`cp #{@@test_file_path} /tmp/host1-sda/test_dir/t1`
|
45
|
+
`cp #{@@test_file_path} /tmp/host1-sda/test_dir/t2`
|
46
|
+
|
47
|
+
make_storages_sync(@@storage, true, true)
|
48
|
+
|
49
|
+
@item1.reload
|
50
|
+
@item2.reload
|
51
|
+
@item3.reload
|
52
|
+
@item4.reload
|
53
|
+
assert_equal 'error', @item1.status
|
54
|
+
assert_equal 'ready', @item2.status
|
55
|
+
assert_equal 'ready', @item3.status
|
56
|
+
assert_equal 'error', @item4.status
|
57
|
+
size = `du -sb #{@@test_file_path} 2>&1`.to_i
|
58
|
+
assert_equal 0, `du -sb /tmp/host1-sda/test1 2>&1`.to_i
|
59
|
+
assert_equal size, `du -sb /tmp/host1-sda/a/b/test2 2>&1`.to_i
|
60
|
+
assert_equal size, `du -sb /tmp/host1-sda/a/b/c/test3 2>&1`.to_i
|
61
|
+
assert_equal 0, `du -sb /tmp/host1-sda/a/b/c/d/test5 2>&1`.to_i
|
62
|
+
assert_equal 0, `du -sb /tmp/host1-sda/a/b/c/d 2>&1`.to_i
|
63
|
+
assert_equal 0, `du -sb tmp/host1-sda/test_dir 2>&1`.to_i
|
64
|
+
end
|
65
|
+
end
|
data/test/var_test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class VarTest < Test::Unit::TestCase
|
4
|
+
should "set and get" do
|
5
|
+
assert_nothing_raised {FC::Var.set('test1', 111)}
|
6
|
+
assert_nothing_raised {FC::Var.set(:test2, '222')}
|
7
|
+
assert_equal '111', FC::Var.get(:test1)
|
8
|
+
assert_equal '222', FC::Var.get('test2')
|
9
|
+
end
|
10
|
+
should "change" do
|
11
|
+
assert_nothing_raised {FC::Var.set('test3', '333')}
|
12
|
+
assert_equal '333', FC::Var.get('test3')
|
13
|
+
FC::Var.set('test3', '3332')
|
14
|
+
assert_equal '3332', FC::Var.get('test3')
|
15
|
+
end
|
16
|
+
should "get all" do
|
17
|
+
assert_nothing_raised {FC::Var.set('test4', '444')}
|
18
|
+
assert_nothing_raised {FC::Var.set('test5', '555')}
|
19
|
+
assert_nothing_raised {FC::Var.set('test6', '666')}
|
20
|
+
vars = FC::Var.get_all
|
21
|
+
assert_equal '444', vars['test4']
|
22
|
+
assert_equal '555', vars[:test5]
|
23
|
+
assert_equal '666', vars[:test6]
|
24
|
+
end
|
25
|
+
should "change and get all" do
|
26
|
+
assert_nothing_raised {FC::Var.set('test7', '777')}
|
27
|
+
vars = FC::Var.get_all
|
28
|
+
assert_equal '777', vars['test7']
|
29
|
+
FC::Var.set('test7', '7772')
|
30
|
+
vars = FC::Var.get_all
|
31
|
+
assert_equal '7772', vars['test7']
|
32
|
+
end
|
33
|
+
end
|
metadata
CHANGED
@@ -1,126 +1,111 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: filecluster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: '0.2'
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- sh
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-07-03 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rb-readline
|
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: mysql2
|
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: :runtime
|
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: bundler
|
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: test-unit
|
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: rake
|
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: shoulda-context
|
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'
|
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'
|
110
97
|
- !ruby/object:Gem::Dependency
|
111
98
|
name: mocha
|
112
99
|
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
100
|
requirements:
|
115
|
-
- -
|
101
|
+
- - '>='
|
116
102
|
- !ruby/object:Gem::Version
|
117
103
|
version: 0.13.3
|
118
104
|
type: :development
|
119
105
|
prerelease: false
|
120
106
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
107
|
requirements:
|
123
|
-
- -
|
108
|
+
- - '>='
|
124
109
|
- !ruby/object:Gem::Version
|
125
110
|
version: 0.13.3
|
126
111
|
description: Distributed storage
|
@@ -155,12 +140,14 @@ files:
|
|
155
140
|
- lib/fc/item_storage.rb
|
156
141
|
- lib/fc/policy.rb
|
157
142
|
- lib/fc/storage.rb
|
143
|
+
- lib/fc/var.rb
|
158
144
|
- lib/fc/version.rb
|
159
145
|
- lib/filecluster.rb
|
160
146
|
- lib/manage.rb
|
161
147
|
- lib/manage/policies.rb
|
162
148
|
- lib/manage/show.rb
|
163
149
|
- lib/manage/storages.rb
|
150
|
+
- lib/manage/var.rb
|
164
151
|
- lib/utils.rb
|
165
152
|
- test/base_test.rb
|
166
153
|
- test/daemon_test.rb
|
@@ -170,37 +157,32 @@ files:
|
|
170
157
|
- test/helper.rb
|
171
158
|
- test/item_test.rb
|
172
159
|
- test/policy_test.rb
|
160
|
+
- test/storage_sync.rb
|
173
161
|
- test/storage_test.rb
|
162
|
+
- test/var_test.rb
|
174
163
|
- test/version_test.rb
|
175
164
|
homepage: ''
|
176
165
|
licenses: []
|
166
|
+
metadata: {}
|
177
167
|
post_install_message:
|
178
168
|
rdoc_options: []
|
179
169
|
require_paths:
|
180
170
|
- lib
|
181
171
|
required_ruby_version: !ruby/object:Gem::Requirement
|
182
|
-
none: false
|
183
172
|
requirements:
|
184
|
-
- -
|
173
|
+
- - '>='
|
185
174
|
- !ruby/object:Gem::Version
|
186
175
|
version: '0'
|
187
|
-
segments:
|
188
|
-
- 0
|
189
|
-
hash: -778580397
|
190
176
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
|
-
none: false
|
192
177
|
requirements:
|
193
|
-
- -
|
178
|
+
- - '>='
|
194
179
|
- !ruby/object:Gem::Version
|
195
180
|
version: '0'
|
196
|
-
segments:
|
197
|
-
- 0
|
198
|
-
hash: -778580397
|
199
181
|
requirements: []
|
200
182
|
rubyforge_project:
|
201
|
-
rubygems_version:
|
183
|
+
rubygems_version: 2.0.3
|
202
184
|
signing_key:
|
203
|
-
specification_version:
|
185
|
+
specification_version: 4
|
204
186
|
summary: Distributed storage
|
205
187
|
test_files:
|
206
188
|
- test/base_test.rb
|
@@ -211,5 +193,7 @@ test_files:
|
|
211
193
|
- test/helper.rb
|
212
194
|
- test/item_test.rb
|
213
195
|
- test/policy_test.rb
|
196
|
+
- test/storage_sync.rb
|
214
197
|
- test/storage_test.rb
|
198
|
+
- test/var_test.rb
|
215
199
|
- test/version_test.rb
|