filecluster 0.1.7 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,4 +1,4 @@
1
- ! что делать с error is?
1
+ пернести захардкоженные настройки
2
2
  периодически удалять пустые папки
3
3
  при создании item-а по папке проверять что папка не пустая
4
4
  добавить в check storage проверку на дорступность урла
@@ -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', :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'},
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 options[:global_wait].to_i
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 options[:cycle_time].to_i
67
+ sleep FC::Var.get('daemon_cycle_time', 30).to_i
70
68
  end
@@ -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 <variable>
41
- Variable:
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
- case command
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
- when 'show'
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?("storages_#{ARGV[1]}".to_sym)
90
- send "storages_#{ARGV[1]}"
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 storages'."
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
@@ -15,12 +15,13 @@ class << FC::Error
15
15
  end
16
16
  end
17
17
 
18
- def run_global_daemon(timeout)
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::DB.query("REPLACE #{FC::DB.prefix}vars SET val='#{FC::Storage.curr_host}', name='global_daemon_host'")
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 1000"
74
+ cond << " LIMIT #{limit}"
73
75
  FC::ItemStorage.where(cond).each do |item_storage|
74
- $tasks[item_storage.storage_name] = [] unless $tasks[item_storage.storage_name]
75
- $tasks[item_storage.storage_name] << {:action => type, :item_storage => item_storage}
76
- $log.debug("task add: type=#{type}, item_storage=#{item_storage.id}")
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
- # 10 tasks per thread, maximum 3 tasks
92
- run_threads_count = (tasks_count/10.0).ceil - threads_count
93
- run_threads_count = 3 if run_threads_count > 3
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
- 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
10
- if r['val'] == FC::Storage.curr_host
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 #{r['val']}")
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 1000"
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
- error 'No available storage', :item_id => row['item_id'] unless storage
56
- FC::Item.new(:id => row['item_id']).make_item_storage(storage, 'copy')
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
@@ -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")
@@ -48,7 +48,11 @@ module FC
48
48
  end
49
49
  end
50
50
 
51
- def DB.init_db
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
@@ -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 prcessed as inplace - local_path is valid path to the item for policy
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)
@@ -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
@@ -1,3 +1,3 @@
1
1
  module FC
2
- VERSION = "0.1.7"
2
+ VERSION = "0.2"
3
3
  end
@@ -7,6 +7,7 @@ require "fc/item"
7
7
  require "fc/policy"
8
8
  require "fc/storage"
9
9
  require "fc/error"
10
+ require "fc/var"
10
11
 
11
12
  module FC
12
13
 
@@ -2,3 +2,4 @@ require 'rb-readline'
2
2
  require "manage/policies"
3
3
  require "manage/storages"
4
4
  require "manage/show"
5
+ require "manage/var"
@@ -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 = name unless create_storages.empty?
89
- policy.copy_storages = name unless copy_storages.empty?
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
@@ -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."
@@ -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
@@ -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
@@ -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 -t 1 -g 1 -h host1") do |stdin, stdout, t|
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, 'Item not deleted after mark_deleted') {@item1.reload}
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
@@ -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.1.7
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-05-22 00:00:00.000000000 Z
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: 1.8.24
183
+ rubygems_version: 2.0.3
202
184
  signing_key:
203
- specification_version: 3
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