filecluster 0.3.7 → 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
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