filecluster 0.1.7 → 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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