filecluster 0.0.3

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/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.yml
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in filecluster.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 sh
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Dstorage
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'dstorage'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install dstorage
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.test_files = FileList["test/*_test.rb"]
8
+ end
9
+
10
+ desc "Run tests"
11
+ task :default => :test
data/TODO ADDED
@@ -0,0 +1,9 @@
1
+ добавить в check storage проверку на дорступность урла
2
+
3
+ алерт на доступность в каждоый политике стораджа на запись
4
+ алерт на доступность стораджей
5
+ алерт: проверка раз в сутки на количество is задержавшихся в статусе delete и copy дольше суток (NOW - time > 86400)
6
+
7
+ bin:
8
+ управление из командной строки: добавление, изменение и получение статуса объектов
9
+ в ручную запускаемая задача синхронизации фс и бд
data/bin/fc-daemon ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
4
+ require 'psych'
5
+ require 'logger'
6
+ require 'optparse'
7
+ require 'filecluster'
8
+ require 'utils'
9
+ require 'daemon'
10
+
11
+ $storages = []
12
+ $tasks = {} # tasks by storage name
13
+ $curr_task = {} # task by storage name
14
+ $tasks_threads = {} # threads by storage name
15
+ $check_threads = {} # threads by storage name
16
+ $exit_signal = false
17
+ $global_daemon_thread = nil
18
+
19
+ default_db_config = File.expand_path(File.dirname(__FILE__))+'/db.yml'
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'},
25
+ :curr_host => {:short => 'h', :full => 'host', :default => FC::Storage.curr_host, :text => "Host for storages, default #{FC::Storage.curr_host}"}
26
+ }
27
+ desc = %q{Run FileCluster daemon.
28
+ Usage: fc-daemon [options]}
29
+ options = option_parser_init(descriptions, desc)
30
+ FC::Storage.instance_variable_set(:@uname, options[:curr_host]) if options[:curr_host] && options[:curr_host] != FC::Storage.curr_host
31
+
32
+ STDOUT.sync = true
33
+ $log = Logger.new(STDOUT)
34
+ $log.formatter = proc do |severity, datetime, progname, msg|
35
+ "[#{datetime}] [#{severity}] [#{Thread.current.object_id}] #{msg}\n"
36
+ end
37
+ $log.level = Logger.const_get(options[:log_level].upcase)
38
+ $log.info('Started')
39
+
40
+ db_options = Psych.load(File.read(options[:config]))
41
+ FC::DB.connect_by_config(db_options.merge(:reconnect => true, :multi_threads => true))
42
+ $log.info('Connected to database')
43
+
44
+ def quit_on_quit
45
+ $log.info('Exit signal')
46
+ $exit_signal = true
47
+ end
48
+ trap("TERM") {quit_on_quit}
49
+ trap("INT") {quit_on_quit}
50
+
51
+ while true do
52
+ if $exit_signal
53
+ $log.debug('wait tasks_threads')
54
+ $tasks_threads.each {|t| t.join}
55
+ if $global_daemon_thread
56
+ $log.debug('wait global_daemon_thread')
57
+ $global_daemon_thread.join
58
+ end
59
+ $log.info('Exit')
60
+ exit
61
+ else
62
+ run_global_daemon options[:global_wait].to_i
63
+ update_storages
64
+ storages_check
65
+ update_tasks
66
+ run_tasks
67
+ end
68
+ $log.debug('sleep')
69
+ sleep options[:cycle_time].to_i
70
+ end
data/bin/fc-manage ADDED
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
4
+ require 'psych'
5
+ require 'logger'
6
+ require 'optparse'
7
+ require 'filecluster'
8
+ require 'utils'
9
+ require 'manage'
10
+
11
+ default_db_config = File.expand_path(File.dirname(__FILE__))+'/db.yml'
12
+ descriptions = {
13
+ :config => {:short => 'c', :full => 'config', :default => default_db_config, :text => "path to db.yml file, default #{default_db_config}"},
14
+ :curr_host => {:short => 'h', :full => 'host', :default => FC::Storage.curr_host, :text => "Host for storages, default #{FC::Storage.curr_host}"}
15
+ }
16
+ commands_help = {
17
+ 'storages' => [
18
+ 'show list and manage storages',
19
+ %q{Usage: fc-manage [options] storages <command>
20
+ Command:
21
+ list show all stotages for all hosts
22
+ show <name> show full info for storage
23
+ add add new storage
24
+ rm <name> delete storage
25
+ }],
26
+ 'policies' => [
27
+ 'show list and manage plicies',
28
+ %q{Usage: fc-manage [options] plicies <command>
29
+ Command:
30
+ list show all plicies
31
+ show <id> show full info for policy
32
+ add add new policy
33
+ rm <id> delete policy
34
+ }],
35
+ 'show' => [
36
+ 'show variable',
37
+ %q{Usage: fc-manage [options] show <variable>
38
+ Variable:
39
+ current_host show current host
40
+ global_daemon show host and uptime where run global daemon
41
+ errors [<count>] show last count (default 10) errors
42
+ host_info [<host>] show info for host (default current host)
43
+ items_info show items statistics
44
+ }]
45
+ }
46
+ desc = %q{Get info and manage for storages, policies and items.
47
+ Usage: fc-manage [options] <command> [<args>]
48
+ Commands:
49
+ }
50
+ commands_help.each{|key, val| desc << " #{key}#{" "*(10-key.size)}#{val[0]}\n"}
51
+ desc << " help show help for commands ('fc-manage help <command>')\n"
52
+ $options = option_parser_init(descriptions, desc)
53
+ FC::Storage.instance_variable_set(:@uname, $options[:curr_host]) if $options[:curr_host] && $options[:curr_host] != FC::Storage.curr_host
54
+ trap("INT", proc {exit})
55
+
56
+ STDOUT.sync = true
57
+ db_options = Psych.load(File.read($options[:config]))
58
+ FC::DB.connect_by_config(db_options.merge(:reconnect => true, :multi_threads => true))
59
+
60
+ command = ARGV[0]
61
+ if ARGV.empty?
62
+ puts $options['optparse']
63
+ exit
64
+ end
65
+
66
+ case command
67
+ when 'help'
68
+ if !ARGV[1]
69
+ puts $options['optparse']
70
+ elsif commands_help[ARGV[1]]
71
+ puts commands_help[ARGV[1]][1]
72
+ else
73
+ puts "'#{command}' is not a fc-manage command. See 'fc-manage --help'."
74
+ end
75
+ when 'show'
76
+ if !ARGV[1]
77
+ puts "Need variable name. See 'fc-manage help show'."
78
+ elsif self.private_methods.member?("show_#{ARGV[1]}".to_sym)
79
+ send "show_#{ARGV[1]}"
80
+ else
81
+ puts "Unknown variable. See 'fc-manage help show'."
82
+ end
83
+ when 'storages'
84
+ if !ARGV[1]
85
+ puts "Need command. See 'fc-manage help storages'."
86
+ elsif self.private_methods.member?("storages_#{ARGV[1]}".to_sym)
87
+ send "storages_#{ARGV[1]}"
88
+ else
89
+ puts "Unknown command. See 'fc-manage help storages'."
90
+ end
91
+ when 'policies'
92
+ if !ARGV[1]
93
+ puts "Need command. See 'fc-manage help policies'."
94
+ elsif self.private_methods.member?("policies_#{ARGV[1]}".to_sym)
95
+ send "policies_#{ARGV[1]}"
96
+ else
97
+ puts "Unknown command. See 'fc-manage help policies'."
98
+ end
99
+ else
100
+ puts "'#{command}' is not a fc-manage command. See 'fc-manage --help'."
101
+ end
102
+
data/bin/fc-setup-db ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
4
+ require 'optparse'
5
+ require 'psych'
6
+ require 'filecluster'
7
+ require 'utils'
8
+ require 'readline'
9
+
10
+ descriptions = {
11
+ :host => {:short => 'h', :full => 'host', :default => 'localhost', :text => 'mysql host name, default "localhost"', :save => true},
12
+ :database => {:short => 'd', :full => 'db', :default => 'fc', :text => 'mysql database, default "fc"', :save => true},
13
+ :username => {:short => 'u', :full => 'user', :default => 'root', :text => 'mysql user, default "root"', :save => true},
14
+ :password => {:short => 'p', :full => 'password', :default => '', :text => 'mysql password, default ""', :save => true},
15
+ :port => {:short => 'P', :full => 'port', :default => '3306', :text => 'mysql port, default "3306"', :save => true},
16
+ :prefix => {:short => 't', :full => 'prefix', :default => '', :text => 'tables prefix, default ""', :save => true},
17
+ :init_tables =>{:short => 'i', :full => 'init', :default => false, :text => 'init tables, default no', :no_val => true}
18
+ }
19
+ desc = %q{Setup FileCluster database connection options.
20
+ Create tables if nessary.
21
+ Usage: fc-init-db [options]}
22
+ options = option_parser_init(descriptions, desc)
23
+ options.delete('optparse')
24
+ trap("INT", proc {exit})
25
+
26
+ puts options.inspect.gsub(/[\{\}\:]/, "").gsub(", ", "\n").gsub(/(.{7,})=>/, "\\1:\t").gsub("=>", ":\t\t")
27
+
28
+ s = Readline.readline("Continue? (y/n) ", false).strip.downcase
29
+ puts ""
30
+ if s == "y" || s == "yes"
31
+ print "Test connection.. "
32
+ FC::DB.connect_by_config(options)
33
+ puts "ok"
34
+ if options[:init_tables]
35
+ print "Make tables.. "
36
+ FC::DB.init_db
37
+ puts "ok"
38
+ end
39
+ print "Save to config.. "
40
+ options.select!{|key, val| descriptions[key][:save]}
41
+ File.open(File.expand_path(File.dirname(__FILE__))+'/db.yml', 'w') do |f|
42
+ f.write(options.to_yaml)
43
+ end
44
+ puts "ok"
45
+ else
46
+ puts "Canceled."
47
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/fc/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["sh"]
6
+ gem.email = ["cntyrf@gmail.com"]
7
+ gem.description = %q{Distributed storage}
8
+ gem.summary = %q{Distributed storage}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "filecluster"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = FC::VERSION
17
+
18
+ gem.add_development_dependency "bundler"
19
+ gem.add_development_dependency "test-unit"
20
+ gem.add_development_dependency "rake"
21
+ gem.add_development_dependency "mysql2"
22
+ gem.add_development_dependency "shoulda-context"
23
+ gem.add_development_dependency "mocha", ">= 0.13.3"
24
+ end
data/lib/daemon.rb ADDED
@@ -0,0 +1,91 @@
1
+ require "date"
2
+ require "daemon/base_thread"
3
+ require "daemon/global_daemon_thread"
4
+ require "daemon/check_thread"
5
+ require "daemon/task_thread"
6
+
7
+ def error(msg, options = {})
8
+ $log.error(msg)
9
+ FC::Error.new(options.merge(:host => FC::Storage.curr_host, :message => msg)).save
10
+ end
11
+
12
+ class << FC::Error
13
+ def raise(msg, options = {})
14
+ error(msg, options)
15
+ end
16
+ end
17
+
18
+ def run_global_daemon(timeout)
19
+ $log.debug('Run global daemon check')
20
+ r = FC::DB.connect.query("SELECT #{FC::DB.prefix}vars.*, UNIX_TIMESTAMP() as curr_time FROM #{FC::DB.prefix}vars WHERE name='global_daemon_host'").first
21
+ if !r || r['curr_time'].to_i - r['time'].to_i > timeout
22
+ $log.debug('Set global daemon host to current')
23
+ FC::DB.connect.query("REPLACE #{FC::DB.prefix}vars SET val='#{FC::Storage.curr_host}', name='global_daemon_host'")
24
+ sleep 1
25
+ r = FC::DB.connect.query("SELECT #{FC::DB.prefix}vars.*, UNIX_TIMESTAMP() as curr_time FROM #{FC::DB.prefix}vars WHERE name='global_daemon_host'").first
26
+ end
27
+ if r['val'] == FC::Storage.curr_host
28
+ if !$global_daemon_thread || !$global_daemon_thread.alive?
29
+ $log.debug("spawn GlobalDaemonThread")
30
+ $global_daemon_thread = GlobalDaemonThread.new(timeout)
31
+ end
32
+ else
33
+ if $global_daemon_thread
34
+ $log.warn("Kill global daemon thread (new host = #{r['host']})")
35
+ $global_daemon_thread.exit
36
+ end
37
+ end
38
+ end
39
+
40
+ def update_storages
41
+ $log.debug('Update storages')
42
+ $storages = FC::Storage.where('host = ?', FC::Storage.curr_host)
43
+ end
44
+
45
+ def storages_check
46
+ $log.debug('Run storages check')
47
+ $check_threads.each do |storage_name, thread|
48
+ if thread.alive?
49
+ error "Storage #{storage_name} check timeout"
50
+ thread.exit
51
+ end
52
+ end
53
+ $storages.each do|storage|
54
+ $log.debug("spawn CheckThread for #{storage.name}")
55
+ $check_threads[storage.name] = CheckThread.new(storage.name)
56
+ end
57
+ end
58
+
59
+ def update_tasks
60
+ $log.debug('Update tasks')
61
+ return if $storages.length == 0
62
+
63
+ def check_tasks(type)
64
+ storages_names = $storages.map{|storage| "'#{storage.name}'"}.join(',')
65
+ cond = "storage_name in (#{storages_names}) AND status='#{type.to_s}'"
66
+ ids = $tasks.map{|storage_name, storage_tasks| storage_tasks.select{|task| task[:action] == type}}.
67
+ flatten.map{|task| task[:item_storage].id}
68
+ $curr_task.map{|storage_name, task| ids << task[:item_storage].id if task && task[:action] == type}
69
+
70
+ cond << "AND id not in (#{ids.join(',')})" if (ids.length > 0)
71
+ FC::ItemStorage.where(cond).each do |item_storage|
72
+ $tasks[item_storage.storage_name] = [] unless $tasks[item_storage.storage_name]
73
+ $tasks[item_storage.storage_name] << {:action => type, :item_storage => item_storage}
74
+ $log.debug("task add: type=#{type}, item_storage=#{item_storage.id}")
75
+ end
76
+ end
77
+
78
+ check_tasks(:delete)
79
+ check_tasks(:copy)
80
+ end
81
+
82
+ def run_tasks
83
+ $log.debug('Run tasks')
84
+ $storages.each do |storage|
85
+ thread = $tasks_threads[storage.name]
86
+ if (!thread || !thread.alive?) && $tasks[storage.name] && $tasks[storage.name].size > 0
87
+ $log.debug("spawn TaskThread for #{storage.name}")
88
+ $tasks_threads[storage.name] = TaskThread.new(storage.name)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,13 @@
1
+ class BaseThread < Thread
2
+ def initialize(*args)
3
+ super(*args) do |*p|
4
+ begin
5
+ self.go(*p)
6
+ rescue Exception => e
7
+ error "#{self.class}: #{e.message}; #{e.backtrace.join(', ')}"
8
+ end
9
+ FC::DB.close
10
+ $log.debug("close #{self.class}")
11
+ end
12
+ end
13
+ end