cloud-crowd 0.3.3 → 0.4.0
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/cloud-crowd.gemspec +6 -6
- data/config/config.example.yml +23 -10
- data/lib/cloud-crowd.rb +4 -4
- data/lib/cloud_crowd/action.rb +24 -23
- data/lib/cloud_crowd/asset_store.rb +3 -1
- data/lib/cloud_crowd/asset_store/cloudfiles_store.rb +41 -0
- data/lib/cloud_crowd/asset_store/s3_store.rb +9 -7
- data/lib/cloud_crowd/models/node_record.rb +27 -26
- data/lib/cloud_crowd/models/work_unit.rb +35 -28
- data/lib/cloud_crowd/node.rb +43 -43
- data/lib/cloud_crowd/schema.rb +7 -7
- data/lib/cloud_crowd/server.rb +35 -30
- data/public/css/admin_console.css +25 -62
- data/public/js/admin_console.js +53 -70
- data/test/acceptance/test_server.rb +14 -16
- data/test/unit/test_action.rb +17 -15
- data/views/operations_center.erb +26 -13
- metadata +94 -59
data/cloud-crowd.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'cloud-crowd'
|
3
|
-
s.version = '0.
|
4
|
-
s.date = '2010-
|
3
|
+
s.version = '0.4.0' # Keep version in sync with cloud-cloud.rb
|
4
|
+
s.date = '2010-03-31'
|
5
5
|
|
6
6
|
s.homepage = "http://wiki.github.com/documentcloud/cloud-crowd"
|
7
7
|
s.summary = "Parallel Processing for the Rest of Us"
|
@@ -27,11 +27,10 @@ Gem::Specification.new do |s|
|
|
27
27
|
'--main' << 'README' <<
|
28
28
|
'--all'
|
29
29
|
|
30
|
-
s.add_dependency 'sinatra', ['
|
31
|
-
s.add_dependency 'activerecord', ['
|
30
|
+
s.add_dependency 'sinatra', ['~> 0.9']
|
31
|
+
s.add_dependency 'activerecord', ['~> 2.3']
|
32
32
|
s.add_dependency 'json', ['>= 1.1.7']
|
33
|
-
s.add_dependency 'rest-client', ['>= 1.
|
34
|
-
s.add_dependency 'right_aws', ['>= 1.10.0']
|
33
|
+
s.add_dependency 'rest-client', ['>= 1.4']
|
35
34
|
s.add_dependency 'thin', ['>= 1.2.4']
|
36
35
|
|
37
36
|
if s.respond_to?(:add_development_dependency)
|
@@ -58,6 +57,7 @@ lib/cloud-crowd.rb
|
|
58
57
|
lib/cloud_crowd/action.rb
|
59
58
|
lib/cloud_crowd/asset_store/filesystem_store.rb
|
60
59
|
lib/cloud_crowd/asset_store/s3_store.rb
|
60
|
+
lib/cloud_crowd/asset_store/cloudfiles_store.rb
|
61
61
|
lib/cloud_crowd/asset_store.rb
|
62
62
|
lib/cloud_crowd/command_line.rb
|
63
63
|
lib/cloud_crowd/exceptions.rb
|
data/config/config.example.yml
CHANGED
@@ -2,20 +2,21 @@
|
|
2
2
|
:central_server: http://localhost:9173
|
3
3
|
|
4
4
|
# The following settings allow you to control the number of workers that can run
|
5
|
-
# on a given node, to prevent the node from becoming overloaded. 'max_workers'
|
5
|
+
# on a given node, to prevent the node from becoming overloaded. 'max_workers'
|
6
6
|
# is a simple cap on the maximum number of workers a node is allowed to run
|
7
7
|
# concurrently. 'max_load' is the maximum (one-minute) load average, above which
|
8
8
|
# a node will refuse to take new work. 'min_free_memory' is the minimum amount
|
9
|
-
# of free RAM (in megabytes) a node is allowed to have, below which no new
|
9
|
+
# of free RAM (in megabytes) a node is allowed to have, below which no new
|
10
10
|
# workers are run. These settings may be used in any combination.
|
11
11
|
:max_workers: 5
|
12
12
|
# :max_load: 5.0
|
13
13
|
# :min_free_memory: 150
|
14
14
|
|
15
15
|
# The storage back-end that you'd like to use for intermediate and final results
|
16
|
-
# of processing. 's3' and '
|
17
|
-
# be used in development, on single-machine installations,
|
18
|
-
# If you *are* developing an action, filesystem is certainly
|
16
|
+
# of processing. 's3', 'filesystem', and 'cloudfiles' are supported.
|
17
|
+
# 'filesystem' should only be used in development, on single-machine installations,
|
18
|
+
# or networked drives. If you *are* developing an action, filesystem is certainly
|
19
|
+
# faster and easier.
|
19
20
|
:storage: s3
|
20
21
|
|
21
22
|
# Please provide your AWS credentials for S3 storage of job output.
|
@@ -29,22 +30,34 @@
|
|
29
30
|
:s3_bucket: [your CloudCrowd bucket]
|
30
31
|
:s3_authentication: no
|
31
32
|
|
32
|
-
#
|
33
|
+
# Cloudfiles
|
34
|
+
:cloudfiles_username: [your Rackspace Cloud Files username]
|
35
|
+
:cloudfiles_api_key: [your Rackspace Cloud Files API key]
|
36
|
+
:cloudfiles_container: [your Rackspace Cloud Files container]
|
37
|
+
|
38
|
+
# The following settings configure local paths. 'local_storage_path' is the
|
33
39
|
# directory in which all files will be saved if you're using the 'filesystem'
|
34
|
-
# storage. 'log_path' and 'pid_path' are the directories in which daemonized
|
35
|
-
# servers and nodes will store their process ids and log files. The default
|
40
|
+
# storage. 'log_path' and 'pid_path' are the directories in which daemonized
|
41
|
+
# servers and nodes will store their process ids and log files. The default
|
36
42
|
# values are listed.
|
37
43
|
# :local_storage_path: /tmp/cloud_crowd_storage
|
38
44
|
# :log_path: log
|
39
45
|
# :pid_path: tmp/pids
|
40
46
|
|
41
|
-
# Use HTTP Basic Auth for all requests? (Includes all internal worker requests
|
42
|
-
# to the central server). If yes, specify the login and password that all
|
47
|
+
# Use HTTP Basic Auth for all requests? (Includes all internal worker requests
|
48
|
+
# to the central server). If yes, specify the login and password that all
|
43
49
|
# requests must provide for authentication.
|
44
50
|
:http_authentication: no
|
45
51
|
:login: [your login name]
|
46
52
|
:password: [your password]
|
47
53
|
|
54
|
+
# Disable all the default built-in actions
|
55
|
+
# :disable_default_actions: true
|
56
|
+
|
57
|
+
# Disable specific actions for the node
|
58
|
+
# Use this if you want to disable a limited number of actions
|
59
|
+
# :disabled_actions: ['word_count']
|
60
|
+
|
48
61
|
# By default, CloudCrowd looks for installed actions inside the 'actions'
|
49
62
|
# subdirectory of this configuration folder. 'actions_path' allows you to load
|
50
63
|
# additional actions from a location of your choice.
|
data/lib/cloud-crowd.rb
CHANGED
@@ -4,10 +4,9 @@ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
|
4
4
|
|
5
5
|
# Common Gems:
|
6
6
|
require 'rubygems'
|
7
|
-
gem 'activerecord'
|
7
|
+
gem 'activerecord', '~> 2.0'
|
8
8
|
gem 'json'
|
9
9
|
gem 'rest-client'
|
10
|
-
gem 'right_aws'
|
11
10
|
gem 'sinatra'
|
12
11
|
gem 'thin'
|
13
12
|
|
@@ -20,6 +19,7 @@ autoload :FileUtils, 'fileutils'
|
|
20
19
|
autoload :JSON, 'json'
|
21
20
|
autoload :RestClient, 'rest_client'
|
22
21
|
autoload :RightAws, 'right_aws'
|
22
|
+
autoload :CloudFiles, 'cloudfiles'
|
23
23
|
autoload :Sinatra, 'sinatra'
|
24
24
|
autoload :Thin, 'thin'
|
25
25
|
autoload :YAML, 'yaml'
|
@@ -44,7 +44,7 @@ module CloudCrowd
|
|
44
44
|
autoload :WorkUnit, 'cloud_crowd/models'
|
45
45
|
|
46
46
|
# Keep this version in sync with the gemspec.
|
47
|
-
VERSION = '0.
|
47
|
+
VERSION = '0.4.0'
|
48
48
|
|
49
49
|
# Increment the schema version when there's a backwards incompatible change.
|
50
50
|
SCHEMA_VERSION = 3
|
@@ -166,7 +166,7 @@ module CloudCrowd
|
|
166
166
|
|
167
167
|
# Retrieve the list of every installed Action for this node or server.
|
168
168
|
def action_paths
|
169
|
-
default_actions = Dir["#{ROOT}/actions/*.rb"]
|
169
|
+
default_actions = config[:disable_default_actions] ? [] : Dir["#{ROOT}/actions/*.rb"]
|
170
170
|
installed_actions = Dir["#{@config_path}/actions/*.rb"]
|
171
171
|
custom_actions = CloudCrowd.config[:actions_path] ? Dir["#{CloudCrowd.config[:actions_path]}/*.rb"] : []
|
172
172
|
default_actions + installed_actions + custom_actions
|
data/lib/cloud_crowd/action.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module CloudCrowd
|
2
|
-
|
2
|
+
|
3
3
|
# As you write your custom actions, have them inherit from CloudCrowd::Action.
|
4
|
-
# All actions must implement a +process+ method, which should return a
|
4
|
+
# All actions must implement a +process+ method, which should return a
|
5
5
|
# JSON-serializable object that will be used as the output for the work unit.
|
6
6
|
# See the default actions for examples.
|
7
7
|
#
|
@@ -16,11 +16,11 @@ module CloudCrowd
|
|
16
16
|
# Note that Actions inherit a backticks (`) method that raises an Exception
|
17
17
|
# if the external command fails.
|
18
18
|
class Action
|
19
|
-
|
19
|
+
|
20
20
|
FILE_URL = /\Afile:\/\//
|
21
|
-
|
21
|
+
|
22
22
|
attr_reader :input, :input_path, :file_name, :options, :work_directory
|
23
|
-
|
23
|
+
|
24
24
|
# Initializing an Action sets up all of the read-only variables that
|
25
25
|
# form the bulk of the API for action subclasses. (Paths to read from and
|
26
26
|
# write to). It creates the +work_directory+ and moves into it.
|
@@ -34,17 +34,17 @@ module CloudCrowd
|
|
34
34
|
parse_input
|
35
35
|
download_input
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
# Each Action subclass must implement a +process+ method, overriding this.
|
39
39
|
def process
|
40
40
|
raise NotImplementedError, "CloudCrowd::Actions must override 'process' with their own processing code."
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
# Download a file to the specified path.
|
44
44
|
def download(url, path)
|
45
45
|
`curl -s "#{url}" > "#{path}"`
|
46
46
|
return path
|
47
|
-
# The previous implementation is below, and, although it would be
|
47
|
+
# The previous implementation is below, and, although it would be
|
48
48
|
# wonderful not to shell out, RestClient wasn't handling URLs with encoded
|
49
49
|
# entities (%20, for example), and doesn't let you download to a given
|
50
50
|
# location. Getting a RestClient patch in would be ideal.
|
@@ -56,21 +56,21 @@ module CloudCrowd
|
|
56
56
|
# FileUtils.mv resp.file.path, path
|
57
57
|
# end
|
58
58
|
end
|
59
|
-
|
60
|
-
# Takes a local filesystem path, saves the file to S3, and returns the
|
61
|
-
# public (or authenticated) url on S3 where the file can be accessed.
|
59
|
+
|
60
|
+
# Takes a local filesystem path, saves the file to S3, and returns the
|
61
|
+
# public (or authenticated) url on S3 where the file can be accessed.
|
62
62
|
def save(file_path)
|
63
63
|
save_path = File.join(storage_prefix, File.basename(file_path))
|
64
64
|
@store.save(file_path, save_path)
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
# After the Action has finished, we remove the work directory and return
|
68
68
|
# to the root directory (where workers run by default).
|
69
69
|
def cleanup_work_directory
|
70
70
|
FileUtils.rm_r(@work_directory) if File.exists?(@work_directory)
|
71
71
|
end
|
72
|
-
|
73
|
-
# Actions have a backticks command that raises a CommandFailed exception
|
72
|
+
|
73
|
+
# Actions have a backticks command that raises a CommandFailed exception
|
74
74
|
# on failure, so that processing doesn't just blithely continue.
|
75
75
|
def `(command)
|
76
76
|
result = super(command)
|
@@ -78,17 +78,18 @@ module CloudCrowd
|
|
78
78
|
raise Error::CommandFailed.new(result, exit_code) unless exit_code == 0
|
79
79
|
result
|
80
80
|
end
|
81
|
-
|
82
|
-
|
81
|
+
|
82
|
+
|
83
83
|
private
|
84
|
-
|
84
|
+
|
85
85
|
# Convert an unsafe URL into a filesystem-friendly filename.
|
86
86
|
def safe_filename(url)
|
87
|
+
url.sub!(/\?.*\Z/, '')
|
87
88
|
ext = File.extname(url)
|
88
89
|
name = URI.unescape(File.basename(url)).gsub(/[^a-zA-Z0-9_\-.]/, '-').gsub(/-+/, '-')
|
89
90
|
File.basename(name, ext).gsub('.', '-') + ext
|
90
91
|
end
|
91
|
-
|
92
|
+
|
92
93
|
# The directory prefix to use for both local and S3 storage.
|
93
94
|
# [action]/job_[job_id]/unit_[work_unit_it]
|
94
95
|
def storage_prefix
|
@@ -98,18 +99,18 @@ module CloudCrowd
|
|
98
99
|
path_parts << "unit_#{@work_unit_id}" if @work_unit_id
|
99
100
|
@storage_prefix ||= File.join(path_parts)
|
100
101
|
end
|
101
|
-
|
102
|
+
|
102
103
|
# If we think that the input is JSON, replace it with the parsed form.
|
103
104
|
# It would be great if the JSON module had an is_json? method.
|
104
105
|
def parse_input
|
105
106
|
return unless ['[', '{'].include? @input[0..0]
|
106
107
|
@input = JSON.parse(@input) rescue @input
|
107
108
|
end
|
108
|
-
|
109
|
+
|
109
110
|
def input_is_url?
|
110
111
|
!URI.parse(@input).scheme.nil? rescue false
|
111
112
|
end
|
112
|
-
|
113
|
+
|
113
114
|
# If the input is a URL, download the file before beginning processing.
|
114
115
|
def download_input
|
115
116
|
return unless input_is_url?
|
@@ -119,7 +120,7 @@ module CloudCrowd
|
|
119
120
|
download(@input, @input_path)
|
120
121
|
end
|
121
122
|
end
|
122
|
-
|
123
|
+
|
123
124
|
end
|
124
|
-
|
125
|
+
|
125
126
|
end
|
@@ -14,12 +14,14 @@ module CloudCrowd
|
|
14
14
|
|
15
15
|
autoload :S3Store, 'cloud_crowd/asset_store/s3_store'
|
16
16
|
autoload :FilesystemStore, 'cloud_crowd/asset_store/filesystem_store'
|
17
|
+
autoload :CloudfilesStore, 'cloud_crowd/asset_store/cloudfiles_store'
|
17
18
|
|
18
19
|
# Configure the AssetStore with the specific storage implementation
|
19
20
|
# specified by 'storage' in <tt>config.yml</tt>.
|
20
21
|
case CloudCrowd.config[:storage]
|
21
|
-
when 's3' then include S3Store
|
22
22
|
when 'filesystem' then include FilesystemStore
|
23
|
+
when 's3' then include S3Store
|
24
|
+
when 'cloudfiles' then include CloudfilesStore
|
23
25
|
else raise Error::StorageNotFound, "#{CloudCrowd.config[:storage]} is not a valid storage back end"
|
24
26
|
end
|
25
27
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
gem 'cloudfiles'
|
2
|
+
|
3
|
+
module CloudCrowd
|
4
|
+
class AssetStore
|
5
|
+
|
6
|
+
# The CloudFilesStore is an implementation of an AssetStore that uses a Rackspace Cloud
|
7
|
+
module CloudfilesStore
|
8
|
+
|
9
|
+
# Configure Rackspace Cloud and connect
|
10
|
+
def setup
|
11
|
+
username = CloudCrowd.config[:cloudfiles_username]
|
12
|
+
api_key = CloudCrowd.config[:cloudfiles_api_key]
|
13
|
+
container = CloudCrowd.config[:cloudfiles_container]
|
14
|
+
valid_conf = [username, api_key, container].all? {|s| s.is_a? String }
|
15
|
+
raise Error::MissingConfiguration, "A Rackspace Cloud Files account must be configured in 'config.yml' before 'cloudfiles' storage can be used" unless valid_conf
|
16
|
+
|
17
|
+
@cloud = CloudFiles::Connection.new(username, api_key)
|
18
|
+
@container = @cloud.container container
|
19
|
+
end
|
20
|
+
|
21
|
+
# Save a finished file from local storage to Cloud Files.
|
22
|
+
def save(local_path, save_path)
|
23
|
+
object = @container.create_object save_path, true
|
24
|
+
object.load_from_filename local_path
|
25
|
+
object.public_url
|
26
|
+
end
|
27
|
+
|
28
|
+
# Remove all of a Job's resulting files from Cloud Files, both intermediate and finished.
|
29
|
+
def cleanup(job)
|
30
|
+
@container.objects(:prefix => "#{job.action}/job_#{job.id}").each do |object|
|
31
|
+
begin
|
32
|
+
@container.delete_object object
|
33
|
+
rescue
|
34
|
+
log "failed to delete #{job.action}/job_#{job.id}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
gem 'right_aws'
|
2
|
+
|
1
3
|
module CloudCrowd
|
2
4
|
class AssetStore
|
3
|
-
|
5
|
+
|
4
6
|
# The S3Store is an implementation of an AssetStore that uses a bucket
|
5
7
|
# on S3 for all resulting files.
|
6
8
|
module S3Store
|
7
|
-
|
9
|
+
|
8
10
|
# Configure authentication and establish a connection to S3, first thing.
|
9
11
|
def setup
|
10
12
|
@use_auth = CloudCrowd.config[:s3_authentication]
|
@@ -18,8 +20,8 @@ module CloudCrowd
|
|
18
20
|
@bucket = @s3.bucket(bucket_name)
|
19
21
|
@bucket = @s3.bucket(bucket_name, true) unless @bucket
|
20
22
|
end
|
21
|
-
|
22
|
-
# Save a finished file from local storage to S3. Save it publicly unless
|
23
|
+
|
24
|
+
# Save a finished file from local storage to S3. Save it publicly unless
|
23
25
|
# we're configured to use S3 authentication. Authenticated links expire
|
24
26
|
# after one day by default.
|
25
27
|
def save(local_path, save_path)
|
@@ -31,13 +33,13 @@ module CloudCrowd
|
|
31
33
|
@bucket.key(save_path).public_link
|
32
34
|
end
|
33
35
|
end
|
34
|
-
|
36
|
+
|
35
37
|
# Remove all of a Job's resulting files from S3, both intermediate and finished.
|
36
38
|
def cleanup(job)
|
37
39
|
@bucket.delete_folder("#{job.action}/job_#{job.id}")
|
38
40
|
end
|
39
|
-
|
41
|
+
|
40
42
|
end
|
41
|
-
|
43
|
+
|
42
44
|
end
|
43
45
|
end
|
@@ -1,24 +1,25 @@
|
|
1
1
|
module CloudCrowd
|
2
2
|
|
3
|
-
# A NodeRecord is the central server's record of a Node running remotely. We
|
3
|
+
# A NodeRecord is the central server's record of a Node running remotely. We
|
4
4
|
# can use it to assign WorkUnits to the Node, and keep track of its status.
|
5
5
|
# When a Node exits, it destroys this record.
|
6
6
|
class NodeRecord < ActiveRecord::Base
|
7
|
-
|
7
|
+
|
8
8
|
has_many :work_units
|
9
|
-
|
9
|
+
|
10
10
|
validates_presence_of :host, :ip_address, :port, :enabled_actions
|
11
|
-
|
11
|
+
|
12
12
|
after_destroy :redistribute_work_units
|
13
|
-
|
13
|
+
|
14
14
|
# Available Nodes haven't used up their maxiumum number of workers yet.
|
15
15
|
named_scope :available, {
|
16
16
|
:conditions => ['(max_workers is null or (select count(*) from work_units where node_record_id = node_records.id) < max_workers)'],
|
17
17
|
:order => 'updated_at asc'
|
18
18
|
}
|
19
|
-
|
20
|
-
# Register a Node with the central server.
|
21
|
-
# Node
|
19
|
+
|
20
|
+
# Register a Node with the central server. This happens periodically
|
21
|
+
# (once every `Node::CHECK_IN_INTERVAL` seconds). Nodes will be de-registered
|
22
|
+
# if they checked in within a reasonable interval.
|
22
23
|
def self.check_in(params, request)
|
23
24
|
attrs = {
|
24
25
|
:ip_address => request.ip,
|
@@ -29,15 +30,15 @@ module CloudCrowd
|
|
29
30
|
}
|
30
31
|
self.find_or_create_by_host(params[:host]).update_attributes!(attrs)
|
31
32
|
end
|
32
|
-
|
33
|
+
|
33
34
|
# Dispatch a WorkUnit to this node. Places the node at back at the end of
|
34
35
|
# the rotation. If we fail to send the WorkUnit, we consider the node to be
|
35
36
|
# down, and remove this record, freeing up all of its checked-out work units.
|
36
|
-
# If the Node responds that it's overloaded, we mark it as busy. Returns
|
37
|
+
# If the Node responds that it's overloaded, we mark it as busy. Returns
|
37
38
|
# true if the WorkUnit was dispatched successfully.
|
38
39
|
def send_work_unit(unit)
|
39
40
|
result = node['/work'].post(:work_unit => unit.to_json)
|
40
|
-
unit.assign_to(self, JSON.parse(result)['pid'])
|
41
|
+
unit.assign_to(self, JSON.parse(result.body)['pid'])
|
41
42
|
touch && true
|
42
43
|
rescue RestClient::RequestFailed => e
|
43
44
|
raise e unless e.http_code == 503 && e.http_body == Node::OVERLOADED_MESSAGE
|
@@ -46,45 +47,45 @@ module CloudCrowd
|
|
46
47
|
# Couldn't post to node, assume it's gone away.
|
47
48
|
destroy && false
|
48
49
|
end
|
49
|
-
|
50
|
+
|
50
51
|
# What Actions is this Node able to run?
|
51
52
|
def actions
|
52
53
|
@actions ||= enabled_actions.split(',')
|
53
54
|
end
|
54
|
-
|
55
|
-
# Is this Node too busy for more work? Determined by number of workers, or
|
55
|
+
|
56
|
+
# Is this Node too busy for more work? Determined by number of workers, or
|
56
57
|
# the Node's load average, as configured in config.yml.
|
57
58
|
def busy?
|
58
59
|
busy || (max_workers && work_units.count >= max_workers)
|
59
60
|
end
|
60
|
-
|
61
|
+
|
61
62
|
# The URL at which this Node may be reached.
|
62
63
|
# TODO: Make sure that the host actually has externally accessible DNS.
|
63
64
|
def url
|
64
65
|
@url ||= "http://#{host}:#{port}"
|
65
66
|
end
|
66
|
-
|
67
|
-
# Keep a RestClient::Resource handy for contacting the Node, including
|
67
|
+
|
68
|
+
# Keep a RestClient::Resource handy for contacting the Node, including
|
68
69
|
# HTTP authentication, if configured.
|
69
70
|
def node
|
70
71
|
@node ||= RestClient::Resource.new(url, CloudCrowd.client_options)
|
71
72
|
end
|
72
|
-
|
73
|
+
|
73
74
|
# The printable status of the Node.
|
74
75
|
def display_status
|
75
76
|
busy? ? 'busy' : 'available'
|
76
77
|
end
|
77
|
-
|
78
|
+
|
78
79
|
# A list of the process ids of the workers currently being run by the Node.
|
79
80
|
def worker_pids
|
80
81
|
work_units.all(:select => 'worker_pid').map(&:worker_pid)
|
81
82
|
end
|
82
|
-
|
83
|
+
|
83
84
|
# Release all of this Node's WorkUnits for other nodes to take.
|
84
85
|
def release_work_units
|
85
86
|
WorkUnit.update_all('node_record_id = null, worker_pid = null', "node_record_id = #{id}")
|
86
87
|
end
|
87
|
-
|
88
|
+
|
88
89
|
# The JSON representation of a NodeRecord includes its worker_pids.
|
89
90
|
def to_json(opts={})
|
90
91
|
{ 'host' => host,
|
@@ -92,16 +93,16 @@ module CloudCrowd
|
|
92
93
|
'status' => display_status
|
93
94
|
}.to_json
|
94
95
|
end
|
95
|
-
|
96
|
-
|
96
|
+
|
97
|
+
|
97
98
|
private
|
98
|
-
|
99
|
-
# When a Node exits, release its WorkUnits and redistribute them to others.
|
99
|
+
|
100
|
+
# When a Node exits, release its WorkUnits and redistribute them to others.
|
100
101
|
# Redistribute in a separate thread to avoid delaying shutdown.
|
101
102
|
def redistribute_work_units
|
102
103
|
release_work_units
|
103
104
|
Thread.new { WorkUnit.distribute_to_nodes }
|
104
105
|
end
|
105
|
-
|
106
|
+
|
106
107
|
end
|
107
108
|
end
|