filecluster 0.3.7 → 0.3.8

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.
data/README.md CHANGED
@@ -5,6 +5,7 @@ A set of scripts to manage files on multiple dc/host/drive.
5
5
  Item (storage unit)- file or folder.
6
6
  Storage - folder, usually separate drive.
7
7
  Policy - rule for selecting storage to store the item and сreate copies.
8
+ Copy rule - additional rule for copies сreate.
8
9
 
9
10
  Daemon monitors the availability of each storage and copy files between them.
10
11
 
@@ -31,7 +32,20 @@ Selecting available storage to store item by policy.create_storages (from left t
31
32
 
32
33
  ## Copy policy
33
34
 
34
- Selecting available storage to copy item by storage.copy_storages (from left to right).
35
+ Used first available storage.
36
+ First try copy rules (copy_storages field, from left to right).
37
+ After that storage.copy_storages (from left to right).
38
+
39
+ Copy rule is ruby expression.
40
+ Can be used the following variables:
41
+
42
+ * item_id
43
+ * size
44
+ * item_copies
45
+ * name
46
+ * tag
47
+ * dir
48
+ * src_storage (FC::Storage instance)
35
49
 
36
50
  ## Variables
37
51
 
@@ -46,6 +46,16 @@ Command:
46
46
  errors [<count>] show last count (default 10) errors
47
47
  host_info [<host>] show info for host (default current host)
48
48
  items_info show items statistics
49
+ }],
50
+ 'copy_rules' => [
51
+ 'show and manage copy rules',
52
+ %q{Usage: fc-manage [options] copy_rules <command>
53
+ Command:
54
+ list show all copy rules
55
+ show <id> show full info for copy rule
56
+ add add new copy rule
57
+ rm <id> delete copy rule
58
+ change <id> change copy rules attributes
49
59
  }],
50
60
  'var' => [
51
61
  'show and change FC::Var',
@@ -33,7 +33,7 @@ class GlobalDaemonThread < BaseThread
33
33
  all_policies.each do |policy|
34
34
  next if policy.copies.to_i < 2
35
35
  copies = (1..policy.copies.to_i-1).to_a.join(',')
36
- 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 "+
36
+ sql = "SELECT i.id as item_id, i.size, i.copies as item_copies, i.name, i.tag, i.dir, GROUP_CONCAT(ist.storage_name ORDER BY ist.id) as storages "+
37
37
  "FROM #{FC::Item.table_name} as i, #{FC::ItemStorage.table_name} as ist WHERE i.policy_id = #{policy.id} AND "+
38
38
  "ist.item_id = i.id AND i.copies IN (#{copies}) AND i.status = 'ready' AND ist.status <> 'delete' GROUP BY i.id LIMIT #{limit}"
39
39
  r = FC::DB.query(sql)
@@ -46,8 +46,23 @@ class GlobalDaemonThread < BaseThread
46
46
  $log.warn("GlobalDaemonThread: ItemStorage count >= policy.copies for item #{row['item_id']}")
47
47
  else
48
48
  src_storage = all_storages.detect{|s| item_storages.first == s.name}
49
- storage = src_storage.get_proper_storage_for_copy(row['size'], item_storages) if src_storage
49
+ if src_storage
50
+ storage = FC::CopyRule.get_proper_storage_for_copy(
51
+ :item_id => row['item_id'],
52
+ :size => row['size'],
53
+ :item_copies => row['item_copies'],
54
+ :name => row['name'],
55
+ :tag => row['tag'],
56
+ :dir => row['dir'].to_i == 1,
57
+ :src_storage => src_storage,
58
+ :exclude => item_storages
59
+ )
60
+ storage = src_storage.get_proper_storage_for_copy(row['size'], item_storages) unless storage
61
+ end
50
62
  if storage
63
+ puts row
64
+ puts src_storage.name+'>'
65
+ puts storage.name
51
66
  FC::Item.new(:id => row['item_id']).make_item_storage(storage, 'copy')
52
67
  else
53
68
  error 'No available storage', :item_id => row['item_id']
@@ -0,0 +1,79 @@
1
+ # encoding: utf-8
2
+
3
+ module FC
4
+ class CopyRule < DbBase
5
+ set_table :copy_rules, 'rule, copy_storages'
6
+
7
+ class << self
8
+ attr_accessor :rules_cache_time, :get_rules_mutex, :copy_storages_cache_time, :get_copy_storages_mutex
9
+ end
10
+ @rules_cache_time = 20 # ttl for rules cache
11
+ @copy_storages_cache_time = 20 # ttl for rule copy_storages
12
+ @get_rules_mutex = Mutex.new
13
+ @get_copy_storages_mutex = Mutex.new
14
+
15
+ def self.all
16
+ get_rules_mutex.synchronize do
17
+ unless @all_rules_cache && Time.new.to_i - @get_all_rules_time.to_i < rules_cache_time
18
+ @get_all_rules_time = Time.new.to_i
19
+ @all_rules_cache = where("1")
20
+ end
21
+ end
22
+ @all_rules_cache
23
+ end
24
+
25
+ def self.check_all(item_id, size, item_copies, name, tag, dir, src_storage)
26
+ all.select do |r|
27
+ r.check(item_id, size, item_copies, name, tag, dir, src_storage)
28
+ end
29
+ end
30
+
31
+ # get available storage for copy
32
+ def self.get_proper_storage_for_copy(options)
33
+ exclude = options[:exclude] || []
34
+ rules = check_all(options[:item_id].to_i, options[:size].to_i, options[:item_copies].to_i, options[:name].to_s, options[:tag].to_s, options[:dir] ? true : false, options[:src_storage])
35
+ result = nil
36
+ rules.each do |rule|
37
+ result = rule.get_copy_storages.select do |storage|
38
+ !exclude.include?(storage.name) && storage.up? && storage.size + options[:size].to_i < storage.size_limit
39
+ end.first
40
+ break if result
41
+ end
42
+ result
43
+ end
44
+
45
+ def get_copy_storages
46
+ self.class.get_copy_storages_mutex.synchronize do
47
+ unless @copy_storages_cache && Time.new.to_i - @get_copy_storages_time.to_i < self.class.copy_storages_cache_time
48
+ @get_copy_storages_time = Time.new.to_i
49
+ names = copy_storages.to_s.split(',').map{|s| "'#{s}'"}.join(',')
50
+ @copy_storages_cache = names.empty? ? [] : FC::Storage.where("name IN (#{names}) ORDER BY FIELD(name, #{names})")
51
+ end
52
+ end
53
+ @copy_storages_cache
54
+ end
55
+
56
+ def check(item_id, size, item_copies, name, tag, dir, src_storage)
57
+ return false unless rule
58
+ $SELF = 4
59
+ r = eval(rule)
60
+ $SELF = 0
61
+ r ? true : false
62
+ end
63
+
64
+ def test
65
+ storage = FC::Storage.new(
66
+ :id => 1,
67
+ :name => 'test_storage',
68
+ :host => 'test_host',
69
+ :path => '/bla/bla',
70
+ :url => 'http://bla',
71
+ :size => 1000,
72
+ :size_limit => 9999,
73
+ :check_time => Time.new.to_i,
74
+ :copy_storages => 'a,b,c'
75
+ )
76
+ check(3, 1, 1, 'test/item', 'tag', false, storage)
77
+ end
78
+ end
79
+ end
@@ -179,6 +179,15 @@ module FC
179
179
  })
180
180
  FC::DB.connect.query("CREATE TRIGGER fc_errors_before_insert BEFORE INSERT on #{@prefix}errors FOR EACH ROW BEGIN #{proc_time} END")
181
181
 
182
+ FC::DB.connect.query(%{
183
+ CREATE TABLE #{@prefix}copy_rules (
184
+ id int NOT NULL AUTO_INCREMENT,
185
+ copy_storages text NOT NULL DEFAULT '',
186
+ rule text DEFAULT NULL,
187
+ PRIMARY KEY (id)
188
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
189
+ })
190
+
182
191
  FC::DB.connect.query(%{
183
192
  CREATE TABLE #{@prefix}vars (
184
193
  name varchar(255) DEFAULT NULL,
@@ -28,11 +28,16 @@ module FC
28
28
  @create_storages_cache
29
29
  end
30
30
 
31
- # get available storage for create by size
32
- def get_proper_storage_for_create(size, exclude = [])
33
- get_create_storages.detect do |storage|
31
+ # get available storages for create by size
32
+ def get_proper_storages_for_create(size, exclude = [])
33
+ get_create_storages.select do |storage|
34
34
  !exclude.include?(storage.name) && storage.up? && storage.size + size < storage.size_limit
35
35
  end
36
36
  end
37
+
38
+ # get available storage for create by size
39
+ def get_proper_storage_for_create(size, exclude = [])
40
+ get_proper_storages_for_create(size, exclude).first
41
+ end
37
42
  end
38
43
  end
@@ -1,3 +1,3 @@
1
1
  module FC
2
- VERSION = "0.3.7"
2
+ VERSION = "0.3.8"
3
3
  end
@@ -6,6 +6,7 @@ require "fc/item_storage"
6
6
  require "fc/item"
7
7
  require "fc/policy"
8
8
  require "fc/storage"
9
+ require "fc/copy_rule"
9
10
  require "fc/error"
10
11
  require "fc/var"
11
12
 
@@ -2,4 +2,5 @@ require 'readline'
2
2
  require "manage/policies"
3
3
  require "manage/storages"
4
4
  require "manage/show"
5
+ require "manage/copy_rules"
5
6
  require "manage/var"
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+ require 'shellwords'
3
+
4
+ def copy_rules_list
5
+ rules = FC::CopyRule.where("1 ORDER BY id")
6
+ if rules.size == 0
7
+ puts "No rules."
8
+ else
9
+ rules.each do |rule|
10
+ puts "##{rule.id}, copy storages: #{rule.copy_storages}, rule: #{rule.rule}"
11
+ end
12
+ end
13
+ end
14
+
15
+ def copy_rules_show
16
+ if rule = find_rule
17
+ #count = FC::DB.query("SELECT count(*) as cnt FROM #{FC::ItemStorage.table_name} WHERE storage_name='#{Mysql2::Client.escape(storage.name)}'").first['cnt']
18
+ puts %Q{Rule
19
+ Id: #{rule.id}
20
+ Copy storages: #{rule.copy_storages}
21
+ Rule: #{rule.rule}}
22
+ end
23
+ end
24
+
25
+ def copy_rules_add
26
+ puts "Add copy rule"
27
+ copy_storages = stdin_read_val('Copy storages')
28
+ storages = FC::Storage.where.map(&:name)
29
+ copy_storages = copy_storages.split(',').select{|s| storages.member?(s.strip)}.join(',').strip
30
+ rule_str = stdin_read_val('Rule')
31
+
32
+ begin
33
+ rule = FC::CopyRule.new(:rule => rule_str, :copy_storages => copy_storages)
34
+ rule.test
35
+ rescue Exception => e
36
+ puts "Error: #{e.message}"
37
+ exit
38
+ end
39
+ puts %Q{\nRule
40
+ Copy storages: #{rule.copy_storages}
41
+ Rule: #{rule.rule}}
42
+ s = Readline.readline("Continue? (y/n) ", false).strip.downcase
43
+ puts ""
44
+ if s == "y" || s == "yes"
45
+ begin
46
+ rule.save
47
+ rescue Exception => e
48
+ puts "Error: #{e.message}"
49
+ exit
50
+ end
51
+ puts "ok"
52
+ else
53
+ puts "Canceled."
54
+ end
55
+ end
56
+
57
+ def copy_rules_rm
58
+ if rule = find_rule
59
+ s = Readline.readline("Continue? (y/n) ", false).strip.downcase
60
+ puts ""
61
+ if s == "y" || s == "yes"
62
+ rule.delete
63
+ puts "ok"
64
+ else
65
+ puts "Canceled."
66
+ end
67
+ end
68
+ end
69
+
70
+ def copy_rules_change
71
+ if rule = find_rule
72
+ puts "Change rule #{rule.id}"
73
+ copy_storages = stdin_read_val("Copy storages (now #{rule.copy_storages})", true)
74
+ storages = FC::Storage.where.map(&:name)
75
+ rule.copy_storages = copy_storages.split(',').select{|s| storages.member?(s.strip)}.join(',').strip unless copy_storages.empty?
76
+ rule_str = stdin_read_val("Rule (now #{rule.rule})", true)
77
+ rule.rule = rule_str unless rule_str.empty?
78
+
79
+ puts %Q{\nRule
80
+ Id: #{rule.id}
81
+ Copy storages: #{rule.copy_storages}
82
+ Rule: #{rule.rule}}
83
+ s = Readline.readline("Continue? (y/n) ", false).strip.downcase
84
+ puts ""
85
+ if s == "y" || s == "yes"
86
+ begin
87
+ rule.test
88
+ rule.save
89
+ rescue Exception => e
90
+ puts "Error: #{e.message}"
91
+ exit
92
+ end
93
+ puts "ok"
94
+ else
95
+ puts "Canceled."
96
+ end
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ def find_rule
103
+ id = ARGV[2]
104
+ rule = FC::CopyRule.where('id = ?', id).first
105
+ puts "Rule #{id} not found." unless rule
106
+ rule
107
+ end
@@ -0,0 +1,80 @@
1
+ require 'helper'
2
+
3
+ class CopyRuleTest < Test::Unit::TestCase
4
+ class << self
5
+ def startup
6
+ @@rules = []
7
+ @@rules << FC::CopyRule.new(:copy_storages => 'rec2-sda,rec3-sda', :rule => 'size < 100')
8
+ @@rules << FC::CopyRule.new(:copy_storages => 'rec1-sda,rec3-sda', :rule => 'name.match(/^test1/)')
9
+ @@rules.each {|rule| rule.save}
10
+
11
+ @@storages = []
12
+ @@storages << FC::Storage.new(:name => 'rec1-sda', :host => 'rec1', :size => 0, :size_limit => 100)
13
+ @@storages << FC::Storage.new(:name => 'rec2-sda', :host => 'rec2', :size => 0, :size_limit => 10)
14
+ @@storages << FC::Storage.new(:name => 'rec3-sda', :host => 'rec3', :size => 0, :size_limit => 100)
15
+ @@storages.each {|storage| storage.save}
16
+ end
17
+ def shutdown
18
+ FC::DB.query("DELETE FROM copy_rules")
19
+ FC::DB.query("DELETE FROM storages")
20
+ end
21
+ end
22
+
23
+ should "rule check" do
24
+ assert_equal true, @@rules[0].test
25
+ assert_equal true, @@rules[0].check(1, 1, 1, '', '', false, nil)
26
+ assert_equal false, @@rules[0].check(1, 100, 1, '', '', false, nil)
27
+ assert_equal false, @@rules[1].test
28
+ assert_equal false, @@rules[1].check(1, 1, 1, '', '', false, nil)
29
+ assert_equal true, @@rules[1].check(1, 1, 1, 'test1/test', '', false, nil)
30
+ end
31
+
32
+ should "get_copy_storages" do
33
+ FC::CopyRule.copy_storages_cache_time = 10
34
+ assert_equal 'rec2-sda,rec3-sda', @@rules[0].get_copy_storages.map(&:name).join(',')
35
+ @@rules[0].copy_storages = 'rec3-sda,rec2-sda'
36
+ @@rules[0].save
37
+ assert_equal 'rec2-sda,rec3-sda', @@rules[0].get_copy_storages.map(&:name).join(',')
38
+ FC::CopyRule.copy_storages_cache_time = 0
39
+ assert_equal 'rec3-sda,rec2-sda', @@rules[0].get_copy_storages.map(&:name).join(',')
40
+ end
41
+
42
+ should "all rules" do
43
+ FC::CopyRule.rules_cache_time = 10
44
+ assert_equal @@rules.map(&:id), FC::CopyRule.all.map(&:id)
45
+ assert_equal 'rec1-sda,rec3-sda', FC::CopyRule.all[1].copy_storages
46
+ @@rules[1].copy_storages = 'rec1-sda,rec2-sda'
47
+ @@rules[1].save
48
+ assert_equal 'rec1-sda,rec3-sda', FC::CopyRule.all[1].copy_storages
49
+ FC::CopyRule.rules_cache_time = 0
50
+ assert_equal 'rec1-sda,rec2-sda', FC::CopyRule.all[1].copy_storages
51
+ end
52
+
53
+ should "check_all" do
54
+ assert_equal [], FC::CopyRule.check_all(1, 100, 1, '', '', false, nil)
55
+ assert_same_elements [@@rules[0].id], FC::CopyRule.check_all(1, 1, 1, '', '', false, nil).map(&:id)
56
+ assert_same_elements [@@rules[0].id, @@rules[1].id], FC::CopyRule.check_all(1, 1, 1, 'test1/test', '', false, nil).map(&:id)
57
+ end
58
+
59
+ should "get_proper_storage_for_copy" do
60
+ @@rules[0].copy_storages = 'rec2-sda,rec3-sda'
61
+ @@rules[1].copy_storages = 'rec1-sda,rec3-sda'
62
+ @@rules[0].save
63
+ @@rules[1].save
64
+ FC::CopyRule.rules_cache_time = 0
65
+ FC::CopyRule.copy_storages_cache_time = 0
66
+
67
+ @@storages.each {|storage| storage.check_time = 0; storage.save}
68
+ assert_nil FC::CopyRule.get_proper_storage_for_copy(:size => 1), 'all storages down'
69
+
70
+ @@storages[0].update_check_time
71
+ assert_nil FC::CopyRule.get_proper_storage_for_copy(:size => 1), 'first storage up, but no rules with storage'
72
+ assert_equal 'rec1-sda', FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test').name, 'first storage up'
73
+ assert_nil FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test', :size => 200), 'first storage full'
74
+ assert_nil FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test', :exclude => ['rec1-sda']), 'exclude'
75
+ @@storages[1].update_check_time
76
+ assert_equal 'rec2-sda', FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test', :size => 5).name, 'second storages up, small file'
77
+ assert_equal 'rec1-sda', FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test', :size => 20).name, 'second storages up, big file'
78
+ assert_nil FC::CopyRule.get_proper_storage_for_copy(:name => 'test1/test', :size => 1000), 'second storages up, big file'
79
+ end
80
+ end
@@ -53,6 +53,9 @@ class DaemonTest < Test::Unit::TestCase
53
53
  @@policy = FC::Policy.new(:create_storages => 'host1-sda,host1-sdb,host1-sdc', :copies => 2, :name => 'policy 1')
54
54
  @@policy.save
55
55
 
56
+ @@rule = FC::CopyRule.new(:copy_storages => 'host1-sdc', :rule => 'name == "bla/bla/test3"')
57
+ @@rule.save
58
+
56
59
  # wait for running fc-daemon
57
60
  Timeout::timeout(5) do
58
61
  while @stotage_checks < @@storages.size
@@ -77,6 +80,7 @@ class DaemonTest < Test::Unit::TestCase
77
80
  @@storages.each {|storage| storage.reload}
78
81
  assert @@storages[0].up?, "Storage #{@@storages[0].name} down"
79
82
  assert @@storages[1].up?, "Storage #{@@storages[1].name} down"
83
+ assert @@storages[2].up?, "Storage #{@@storages[2].name} down"
80
84
 
81
85
  FC::Storage.any_instance.stubs(:host).returns('host1')
82
86
  FC::Storage.stubs(:curr_host).returns('host1')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: filecluster
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
4
+ version: 0.3.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-06-25 00:00:00.000000000 Z
12
+ date: 2014-07-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rb-readline
@@ -152,6 +152,7 @@ files:
152
152
  - lib/daemon/run_tasks_thread.rb
153
153
  - lib/daemon/update_tasks_thread.rb
154
154
  - lib/fc/base.rb
155
+ - lib/fc/copy_rule.rb
155
156
  - lib/fc/db.rb
156
157
  - lib/fc/error.rb
157
158
  - lib/fc/item.rb
@@ -162,12 +163,14 @@ files:
162
163
  - lib/fc/version.rb
163
164
  - lib/filecluster.rb
164
165
  - lib/manage.rb
166
+ - lib/manage/copy_rules.rb
165
167
  - lib/manage/policies.rb
166
168
  - lib/manage/show.rb
167
169
  - lib/manage/storages.rb
168
170
  - lib/manage/var.rb
169
171
  - lib/utils.rb
170
172
  - test/base_test.rb
173
+ - test/copy_rule_test.rb
171
174
  - test/daemon_test.rb
172
175
  - test/db_test.rb
173
176
  - test/error_test.rb
@@ -193,7 +196,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
193
196
  version: '0'
194
197
  segments:
195
198
  - 0
196
- hash: -247520372150803461
199
+ hash: -1801267482625081315
197
200
  required_rubygems_version: !ruby/object:Gem::Requirement
198
201
  none: false
199
202
  requirements:
@@ -202,7 +205,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
202
205
  version: '0'
203
206
  segments:
204
207
  - 0
205
- hash: -247520372150803461
208
+ hash: -1801267482625081315
206
209
  requirements: []
207
210
  rubyforge_project:
208
211
  rubygems_version: 1.8.24
@@ -211,6 +214,7 @@ specification_version: 3
211
214
  summary: Distributed storage
212
215
  test_files:
213
216
  - test/base_test.rb
217
+ - test/copy_rule_test.rb
214
218
  - test/daemon_test.rb
215
219
  - test/db_test.rb
216
220
  - test/error_test.rb