nuri 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.travis.yml +12 -0
- data/CHANGELOG +146 -0
- data/Gemfile +3 -0
- data/LICENSE +28 -0
- data/README.md +64 -0
- data/Rakefile +15 -0
- data/VERSION +1 -0
- data/bin/delete_modules +11 -0
- data/bin/install_agent +18 -0
- data/bin/install_module +65 -0
- data/bin/nuri +519 -0
- data/bin/nuri.old +183 -0
- data/bin/push_model +16 -0
- data/examples/.gitignore +3 -0
- data/examples/bonfire.sfp +95 -0
- data/examples/bonfire/epcc.sfp +43 -0
- data/examples/bonfire/epcc0.sfp +49 -0
- data/examples/bonfire/epcc2.sfp +52 -0
- data/examples/bonfire/epcc2a.sfp +25 -0
- data/examples/bonfire/inria.sfp +72 -0
- data/examples/bonfire/inria0.sfp +49 -0
- data/examples/bonfire/inria2.sfp +71 -0
- data/examples/bonfire/inria2a.sfp +44 -0
- data/examples/bonfire/inria2b.sfp +54 -0
- data/examples/bonfire/inria2c.sfp +62 -0
- data/examples/bonfire/inria2d.sfp +71 -0
- data/examples/bonfire/inria2e.sfp +80 -0
- data/examples/bonfire/main.sfp +33 -0
- data/examples/bonfire/old/bonfire-1-1-1.sfp +76 -0
- data/examples/bonfire/old/bonfire-1-10-1.sfp +77 -0
- data/examples/bonfire/old/bonfire-1-2-1.sfp +58 -0
- data/examples/bonfire/old/bonfire-1-3-1.sfp +61 -0
- data/examples/bonfire/old/bonfire-1-4-1.sfp +64 -0
- data/examples/bonfire/old/bonfire-1-5-1.sfp +67 -0
- data/examples/bonfire/old/bonfire-1-6-1.sfp +82 -0
- data/examples/bonfire/old/bonfire-1-7-1.sfp +82 -0
- data/examples/bonfire/old/bonfire-1-8-1.sfp +79 -0
- data/examples/bonfire/old/bonfire-1-9-1.sfp +83 -0
- data/examples/bonfire/old/wp-test1a.sfp +38 -0
- data/examples/bonfire/old/wp-test1b.sfp +18 -0
- data/examples/bonfire/old/wp-test1c.sfp +7 -0
- data/examples/bonfire/old/wp-test2.sfp +47 -0
- data/examples/bonfire/old3/bonfire-epcc.sfp +57 -0
- data/examples/bonfire/old3/bonfire-inria.sfp +72 -0
- data/examples/bonfire/old3/bonfire-master.sfp +18 -0
- data/examples/bonfire/old3/bonfire.sfp +23 -0
- data/examples/bonfire/old3/bonfire2.sfp +49 -0
- data/examples/bonfire/old3/bonfire3.sfp +76 -0
- data/examples/bonfire/old3/bonfire4.sfp +78 -0
- data/examples/bonfire/old3/bonfire5.sfp +34 -0
- data/examples/bonfire/old3/bonfire5b.sfp +84 -0
- data/examples/bonfire/old3/hpvm6.sfp +22 -0
- data/examples/bonfire/old3/model.json +1 -0
- data/examples/bonfire/old3/test0.sfp +16 -0
- data/examples/bonfire/old3/test1.sfp +5 -0
- data/examples/bonfire/old3/test10.sfp +5 -0
- data/examples/bonfire/old3/test2.sfp +18 -0
- data/examples/bonfire/old3/test3.sfp +10 -0
- data/examples/bonfire/old3/test4.sfp +11 -0
- data/examples/bonfire/old3/test5.sfp +18 -0
- data/examples/bonfire/old3/test6.sfp +19 -0
- data/examples/bonfire/old3/test7.sfp +34 -0
- data/examples/bonfire/old3/test8.sfp +5 -0
- data/examples/bonfire/old3/test9.sfp +16 -0
- data/examples/bonfire/old3/wordpress-test-cluster.sfp +38 -0
- data/examples/bonfire/old3/wordpress-test.sfp +22 -0
- data/examples/bonfire/old3/wp-test-2.sfp +49 -0
- data/examples/bonfire/test.sfp +13 -0
- data/examples/generator.rb +66 -0
- data/examples/hadoop2.sfp +20 -0
- data/examples/hpcloud.sfp +18 -0
- data/examples/run.rb +17 -0
- data/examples/test.inc +0 -0
- data/examples/test.sfp +11 -0
- data/lib/naas/d3.js +5 -0
- data/lib/naas/d3.v3.min.js +5 -0
- data/lib/naas/index.css +0 -0
- data/lib/naas/index.html +18 -0
- data/lib/naas/index.js +18 -0
- data/lib/naas/jquery-1.10.2.min.js +6 -0
- data/lib/naas/jquery.js +6 -0
- data/lib/naas/naas.rb +160 -0
- data/lib/nuri.rb +62 -0
- data/lib/nuri/choreographer.rb +151 -0
- data/lib/nuri/constraint_helper.rb +9 -0
- data/lib/nuri/directory.rb +40 -0
- data/lib/nuri/master.rb +725 -0
- data/lib/nuri/net_helper.rb +65 -0
- data/lib/nuri/orchestrator.rb +224 -0
- data/lib/nuri/server.rb +212 -0
- data/modules/.gitignore +4 -0
- data/modules/apache/apache.rb +255 -0
- data/modules/apache/apache.rb.old +167 -0
- data/modules/apache/apache.sfp +146 -0
- data/modules/apache/apache.sfp.future +100 -0
- data/modules/apache/load_balancer +20 -0
- data/modules/apache/model.json +1 -0
- data/modules/apache/test.sfp +8 -0
- data/modules/aptpackage/aptpackage.rb +82 -0
- data/modules/aptpackage/aptpackage.sfp +5 -0
- data/modules/bonfire/.gitignore +2 -0
- data/modules/bonfire/README.md +12 -0
- data/modules/bonfire/bonfire.rb +60 -0
- data/modules/bonfire/bonfire.sfp +9 -0
- data/modules/bonfire/config.yml +4 -0
- data/modules/bonfire/helper.rb +149 -0
- data/modules/bonfire/stresstest.rb +144 -0
- data/modules/bonfire/test.sfp +8 -0
- data/modules/client/client.rb +22 -0
- data/modules/client/client.sfp +14 -0
- data/modules/cloud/cloud.rb +11 -0
- data/modules/cloud/cloud.sfp +26 -0
- data/modules/file/file.rb +91 -0
- data/modules/file/file.sfp +9 -0
- data/modules/hadoop1/core-site.xml +17 -0
- data/modules/hadoop1/hadoop-env.sh +55 -0
- data/modules/hadoop1/hadoop1.rb +384 -0
- data/modules/hadoop1/hadoop1.sfp +93 -0
- data/modules/hadoop1/hdfs-site.xml +16 -0
- data/modules/hadoop1/mapred-site.xml +17 -0
- data/modules/hadoop2/core-site.xml +31 -0
- data/modules/hadoop2/hadoop-env.sh +77 -0
- data/modules/hadoop2/hadoop2.rb +401 -0
- data/modules/hadoop2/hadoop2.sfp +114 -0
- data/modules/hadoop2/hdfs-site.xml +47 -0
- data/modules/hadoop2/mapred-site.xml +71 -0
- data/modules/hadoop2/ports +14 -0
- data/modules/hadoop2/yarn-env.sh +112 -0
- data/modules/hadoop2/yarn-site.xml +107 -0
- data/modules/hpcloud/.gitignore +2 -0
- data/modules/hpcloud/README.md +16 -0
- data/modules/hpcloud/config.yml +3 -0
- data/modules/hpcloud/example.sfp +18 -0
- data/modules/hpcloud/hpcloud.rb +241 -0
- data/modules/hpcloud/hpcloud.sfp +22 -0
- data/modules/hpcloud/test.sfp +5 -0
- data/modules/install_module +65 -0
- data/modules/machine/machine.rb +95 -0
- data/modules/machine/machine.sfp +9 -0
- data/modules/mockcloud/mockcloud.rb +20 -0
- data/modules/mockcloud/mockcloud.sfp +6 -0
- data/modules/mysql/mysql.rb +118 -0
- data/modules/mysql/mysql.sfp +38 -0
- data/modules/mysql/test.sfp +3 -0
- data/modules/node/node.rb +8 -0
- data/modules/node/node.sfp +7 -0
- data/modules/object/object.rb +7 -0
- data/modules/object/object.sfp +1 -0
- data/modules/os/os.rb +38 -0
- data/modules/os/os.sfp +11 -0
- data/modules/package/package.rb +26 -0
- data/modules/package/package.sfp +22 -0
- data/modules/package/test.sfp +6 -0
- data/modules/service/model.json +1 -0
- data/modules/service/service.rb +50 -0
- data/modules/service/service.sfp +46 -0
- data/modules/service/test.sfp +6 -0
- data/modules/tarpackage/tarpackage.rb +93 -0
- data/modules/tarpackage/tarpackage.sfp +5 -0
- data/modules/vm/vm.rb +8 -0
- data/modules/vm/vm.sfp +18 -0
- data/modules/wordpress/wordpress.rb +98 -0
- data/modules/wordpress/wordpress.sfp +34 -0
- data/modules/wordpresscluster/wordpresscluster.rb +150 -0
- data/modules/wordpresscluster/wordpresscluster.sfp +74 -0
- data/nuri.gemspec +26 -0
- metadata +281 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Nuri::Master::Model
|
4
|
+
@@mutex = Mutex.new
|
5
|
+
@@model = {}
|
6
|
+
|
7
|
+
def self.set(path, data)
|
8
|
+
@@mutex.synchronize {
|
9
|
+
parent_path, key = path.pop_ref
|
10
|
+
if key.nil?
|
11
|
+
@@model[parent_path] = data
|
12
|
+
else
|
13
|
+
parent = @@model.at?(parent_path)
|
14
|
+
fail "#{parent_path} is invalid! - #{parent.class.name}" if !parent.is_a?(Hash)
|
15
|
+
parent[key] = data
|
16
|
+
end
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.get(path)
|
21
|
+
data = nil
|
22
|
+
@@mutex.synchronize {
|
23
|
+
data = @@model.at?(path)
|
24
|
+
}
|
25
|
+
data
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.delete(path)
|
29
|
+
@@mutex.synchronize {
|
30
|
+
parent_path, key = path.pop_ref if path.pop_ref
|
31
|
+
if key.nil?
|
32
|
+
@@model.delete(parent_path)
|
33
|
+
else
|
34
|
+
parent = @@model.at?(parent_path)
|
35
|
+
fail "#{parent_path} is invalid! - #{parent.class.name}" if !parent.is_a?(Hash)
|
36
|
+
parent.delete(key)
|
37
|
+
end
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
data/lib/nuri/master.rb
ADDED
@@ -0,0 +1,725 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
class Nuri::Master
|
4
|
+
include Nuri::Net::Helper
|
5
|
+
include Nuri::Orchestrator
|
6
|
+
include Nuri::Choreographer
|
7
|
+
#include Nuri::Server
|
8
|
+
|
9
|
+
SfpUnknown = Sfp::Unknown.new
|
10
|
+
SfpUndefined = Sfp::Undefined.new
|
11
|
+
|
12
|
+
AgentSchema = '$.Node'
|
13
|
+
CloudSchema = '$.Cloud'
|
14
|
+
VMSchema = '$.VM'
|
15
|
+
|
16
|
+
attr_reader :model
|
17
|
+
|
18
|
+
def initialize(p={})
|
19
|
+
@mutex_vm_updater = Mutex.new
|
20
|
+
@cloudfinder = Sfp::Helper::CloudFinder.new
|
21
|
+
@local_agent = nil
|
22
|
+
|
23
|
+
# set modules directory
|
24
|
+
if p[:modules_dir] and File.directory?(p[:modules_dir])
|
25
|
+
@modules_dir = File.expand_path(p[:modules_dir])
|
26
|
+
elsif ENV['NURI_HOME'].is_a?(String) and ENV['NURI_HOME'].strip.length > 0
|
27
|
+
@modules_dir = File.join(ENV['NURI_HOME'], 'modules')
|
28
|
+
elsif File.directory?(File.expand_path(File.dirname(__FILE__) + '/../../modules'))
|
29
|
+
@modules_dir = File.expand_path(File.dirname(__FILE__) + '/../../modules')
|
30
|
+
elsif File.directory?(File.expand_path('./modules'))
|
31
|
+
@modules_dir = File.expand_path('./modules')
|
32
|
+
else
|
33
|
+
@modules_dir = '/var/nuri/modules'
|
34
|
+
end
|
35
|
+
fail "Invalid modules directory #{@modules_dir}!" if !File.directory?(@modules_dir)
|
36
|
+
|
37
|
+
@sas_post_processor = SASPostProcessor
|
38
|
+
|
39
|
+
set_model(p)
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_model(p={})
|
43
|
+
if p[:model_file]
|
44
|
+
home_dir = File.expand_path File.dirname(p[:model_file])
|
45
|
+
@parser = Sfp::Parser.new({:home_dir => home_dir})
|
46
|
+
@parser.parse File.read(p[:model_file])
|
47
|
+
p[:model] = @parser.root
|
48
|
+
end
|
49
|
+
|
50
|
+
@model = (p.is_a?(Hash) and p[:model].is_a?(Hash) ? p[:model] : {})
|
51
|
+
push_agents_list if @model.length > 0
|
52
|
+
|
53
|
+
# find a list of cloud proxy
|
54
|
+
@model.accept(@cloudfinder.reset)
|
55
|
+
|
56
|
+
# create a set of not-exist VMs' state
|
57
|
+
@map = generate_not_exist_vm_state(false)
|
58
|
+
SASPostProcessor.set_map(@map)
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_plan(p={})
|
62
|
+
# set parameters value to be given to the planner
|
63
|
+
p[:sfp] = create_plan_task(p)
|
64
|
+
p[:sas_post_processor] = SASPostProcessor
|
65
|
+
|
66
|
+
plan = nil
|
67
|
+
planning_time = Benchmark.measure do
|
68
|
+
planner = Sfp::Planner.new
|
69
|
+
plan = planner.solve(p)
|
70
|
+
end
|
71
|
+
puts "Planning time (s): #{planning_time}"
|
72
|
+
|
73
|
+
plan
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_state(p={})
|
77
|
+
state = {}
|
78
|
+
vms = get_vms
|
79
|
+
agents = get_agents
|
80
|
+
|
81
|
+
# push agents list to each agent
|
82
|
+
push_agents_list
|
83
|
+
|
84
|
+
mutex = Mutex.new
|
85
|
+
|
86
|
+
# get state of non-VM nodes
|
87
|
+
(agents.keys - vms.keys).each do |name|
|
88
|
+
Thread.new {
|
89
|
+
node_name = name
|
90
|
+
node_state = get_node_state(node_name, !!p[:push_modules])
|
91
|
+
mutex.synchronize { state[node_name] = node_state }
|
92
|
+
}
|
93
|
+
end
|
94
|
+
total = agents.keys.length - vms.keys.length
|
95
|
+
|
96
|
+
# wait until all threads have finish
|
97
|
+
wait? { (state.length >= total) }
|
98
|
+
|
99
|
+
# assign VMs' address
|
100
|
+
exist_vms, not_exist_vms = update_vms_address(state)
|
101
|
+
|
102
|
+
# get state of existing VM nodes
|
103
|
+
exist_vms.each_key { |name|
|
104
|
+
Thread.new {
|
105
|
+
node_name = name
|
106
|
+
node_state = get_node_state(node_name, !!p[:push_modules])
|
107
|
+
mutex.synchronize { state[node_name] = node_state }
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
# get state of non-existing VM nodes
|
112
|
+
not_exist_vms.each { |name,model|
|
113
|
+
state[name] = get_not_exist_vm_state(model)
|
114
|
+
}
|
115
|
+
|
116
|
+
# wait until all threads have finish
|
117
|
+
wait? { (state.length >= agents.length) }
|
118
|
+
|
119
|
+
# update <vm>.in_cloud value
|
120
|
+
update_cloud_vm_relations(state, vms)
|
121
|
+
|
122
|
+
state
|
123
|
+
end
|
124
|
+
|
125
|
+
protected
|
126
|
+
def create_plan_task(p={})
|
127
|
+
task = get_schemata
|
128
|
+
|
129
|
+
puts "Getting current state [WAIT]".yellow
|
130
|
+
b = Benchmark.measure { task['initial'] = to_state('initial', get_state(p)) }
|
131
|
+
puts "Getting current state [OK] : #{b}".green
|
132
|
+
|
133
|
+
task['initial'].accept(Sfp::Visitor::SfpGenerator.new(task))
|
134
|
+
f1 = Sfp::Helper::SfpFlatten.new
|
135
|
+
task['initial'].accept(f1)
|
136
|
+
|
137
|
+
# modify condition of procedures of each VM's component
|
138
|
+
# modification: add constraint "$.vm.created = true"
|
139
|
+
task['initial'].accept(VMProcedureModifier.new(task['initial']))
|
140
|
+
|
141
|
+
# construct goal state
|
142
|
+
goalgen = GoalGenerator.new
|
143
|
+
goal = Sfp::Helper.deep_clone(get_agents)
|
144
|
+
goal.accept(FinalAttributeRemover)
|
145
|
+
goal.accept(goalgen)
|
146
|
+
task['goal'] = goalgen.results
|
147
|
+
|
148
|
+
# find dead-node, remove from the task, print WARNING to the console
|
149
|
+
dead_nodes = task['initial'].select { |k,v| v.is_a?(Sfp::Unknown) }
|
150
|
+
dead_nodes.each_key { |name|
|
151
|
+
task['initial'].delete(name)
|
152
|
+
task['goal'].keep_if { |k,v| !(k =~ /(\$\.#{name}\.|\$\.#{name}$)/) }
|
153
|
+
puts "[WARN] Removing node #{name} from the task.".red
|
154
|
+
}
|
155
|
+
|
156
|
+
# print the status of goal state
|
157
|
+
puts "Goal state:".yellow
|
158
|
+
goalgen.results.each { |k,v|
|
159
|
+
next if k[0,1] == '_'
|
160
|
+
print "- #{k}: " + Sfp::Helper.sfp_to_s(v['_value']).green
|
161
|
+
print " #{Sfp::Helper.sfp_to_s(f1.results[k])}".red if f1.results.has_key?(k) and
|
162
|
+
f1.results[k] != v['_value']
|
163
|
+
puts ""
|
164
|
+
}
|
165
|
+
|
166
|
+
# add global constraint (if exist)
|
167
|
+
task['global'] = @model['global'] if @model.has_key?('global')
|
168
|
+
|
169
|
+
# add sometime constraint (if exist)
|
170
|
+
task['sometime'] = @model['sometime'] if @model.has_key?('sometime')
|
171
|
+
|
172
|
+
# remove old parent links, and then reconstruct SFP parent links
|
173
|
+
task.accept(ParentEliminator)
|
174
|
+
|
175
|
+
# rebuild SFP data-structure
|
176
|
+
task.accept(Sfp::Visitor::SfpGenerator.new(task))
|
177
|
+
|
178
|
+
task
|
179
|
+
end
|
180
|
+
|
181
|
+
def wait?
|
182
|
+
until yield do
|
183
|
+
sleep 1
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def get_dead_vm_state(model, cloud)
|
188
|
+
s = {'state' => Sfp::Helper.deep_clone(model)}
|
189
|
+
s.accept(VisitorDeadAgentNodeState)
|
190
|
+
s.accept(ParentEliminator)
|
191
|
+
s['state']['created'] = true
|
192
|
+
s['state']['in_cloud'] = cloud
|
193
|
+
s['state']
|
194
|
+
end
|
195
|
+
|
196
|
+
def generate_not_exist_vm_state(with_final_attribute=true)
|
197
|
+
map = {}
|
198
|
+
get_vms.each do |name,model|
|
199
|
+
state = {name => get_not_exist_vm_state(model)}
|
200
|
+
state.accept(FinalAttributeRemover) if not with_final_attribute
|
201
|
+
state.accept(ParentGenerator)
|
202
|
+
flatten = Sfp::Helper::SfpFlatten.new
|
203
|
+
state.accept(flatten)
|
204
|
+
map[name] = {}
|
205
|
+
flatten.results.each { |k,v| map[name][k] = v }
|
206
|
+
end
|
207
|
+
map
|
208
|
+
end
|
209
|
+
|
210
|
+
def get_not_exist_vm_state(model)
|
211
|
+
s = {'state' => Sfp::Helper.deep_clone(model)}
|
212
|
+
s.accept(VisitorNotExistNodeState)
|
213
|
+
s.accept(ParentEliminator)
|
214
|
+
s['state']['created'] = false
|
215
|
+
s['state']['in_cloud'] = {'_context' => 'null', '_value' => CloudSchema}
|
216
|
+
s['state']
|
217
|
+
end
|
218
|
+
|
219
|
+
def update_cloud_vm_relations(state, vms)
|
220
|
+
@cloudfinder.clouds.each do |cloud|
|
221
|
+
proxy = state.at?(cloud)
|
222
|
+
next if not proxy.is_a?(Hash) or not proxy['vms'].is_a?(Hash)
|
223
|
+
# for each VMs list of a cloud proxy, assign "in_cloud" attribute
|
224
|
+
# to associated VM
|
225
|
+
proxy['vms'].each do |name,data|
|
226
|
+
next if not vms.has_key?(name)
|
227
|
+
if state[name].is_a?(Hash)
|
228
|
+
state[name]['in_cloud'] = cloud
|
229
|
+
elsif state[name].is_a?(Sfp::Unknown)
|
230
|
+
state[name] = get_dead_vm_state(vms[name], cloud)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def update_vms_address(state)
|
237
|
+
exist_vms = not_exist_vms = nil
|
238
|
+
@mutex_vm_updater.synchronize {
|
239
|
+
vms = get_vms
|
240
|
+
|
241
|
+
# Reset sfpAddress, sfpPort, in_cloud of a VM if it's not found in
|
242
|
+
# previously assigned cloud
|
243
|
+
vms.each do |name,model|
|
244
|
+
next if !model['in_cloud'].is_a?(String) or !model['in_cloud'].isref
|
245
|
+
cloud, _ = @cloudfinder.clouds.select { |cloud| model['in_cloud'] == cloud }
|
246
|
+
if !cloud.nil? and !state.at?("#{cloud}.vms").has_key?(name)
|
247
|
+
vms[name]['sfpAddress'] = {'_context'=>'any_value','_isa'=>'$.String'}
|
248
|
+
vms[name]['sfpPort'] = {'_context'=>'any_value','_isa'=>'$.Number'}
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
@cloudfinder.clouds.each do |cloud|
|
253
|
+
proxy = state.at?(cloud)
|
254
|
+
next if not proxy.is_a?(Hash) or not proxy['vms'].is_a?(Hash)
|
255
|
+
# for each VMs list of a cloud proxy, assign the available
|
256
|
+
# ip address
|
257
|
+
proxy['vms'].each do |name,data|
|
258
|
+
if vms.has_key?(name) and data['running']
|
259
|
+
vms[name]['sfpAddress'] = data['ip']
|
260
|
+
vms[name]['sfpPort'] = 1314
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
exist_vms = vms.select { |k,v| v['sfpAddress'].is_a?(String) and v['sfpAddress'] != '' }
|
266
|
+
not_exist_vms = vms.select { |k,v| !exist_vms.has_key?(k) }
|
267
|
+
}
|
268
|
+
[exist_vms, not_exist_vms]
|
269
|
+
end
|
270
|
+
|
271
|
+
def clear_agents_list
|
272
|
+
get_agents.each do |name,model|
|
273
|
+
begin
|
274
|
+
next if not model['sfpAddress'].is_a?(String)
|
275
|
+
address = model['sfpAddress'].to_s.strip
|
276
|
+
port = model['sfpPort'].to_i
|
277
|
+
next if address == '' or port <= 0
|
278
|
+
|
279
|
+
code, _ = delete_data(address, port, '/agents')
|
280
|
+
fail "Bad response: #{code}" if code != '200'
|
281
|
+
rescue Exception => exp
|
282
|
+
$stderr.puts "Cannot delete agents data on #{name} - #{model['sfpAddress']}:#{model['sfpPort']} - #{exp}\n#{exp.backtrace.join("\n")}"
|
283
|
+
return false
|
284
|
+
end
|
285
|
+
end
|
286
|
+
true
|
287
|
+
end
|
288
|
+
|
289
|
+
def push_agents_list
|
290
|
+
begin
|
291
|
+
agents = {}
|
292
|
+
# generate agents list
|
293
|
+
get_agents.each do |name, model|
|
294
|
+
next if not model['sfpAddress'].is_a?(String)
|
295
|
+
address = model['sfpAddress'].to_s.strip
|
296
|
+
port = model['sfpPort'].to_s.strip.to_i
|
297
|
+
next if address == '' or port <= 0
|
298
|
+
agents[name] = {:sfpAddress => address, :sfpPort => port}
|
299
|
+
end
|
300
|
+
data = {'agents' => JSON.generate(agents)}
|
301
|
+
|
302
|
+
# send the list to all agents
|
303
|
+
agents.each do |name, agent|
|
304
|
+
code, _ = put_data(agent[:sfpAddress], agent[:sfpPort], '/agents', data, 5, 20)
|
305
|
+
raise Exception, "Push agents list to #{agent[:sfpAddress]}:#{agent[:sfpPort]} [Failed]" if code.to_i != 200
|
306
|
+
end
|
307
|
+
return true
|
308
|
+
rescue Exception => exp
|
309
|
+
end
|
310
|
+
false
|
311
|
+
end
|
312
|
+
|
313
|
+
def push_modules(agent_model, address=nil, port=nil)
|
314
|
+
if address.nil? or port.nil?
|
315
|
+
return false if !agent_model.is_a?(Hash) or !agent_model['sfpAddress'].is_a?(String)
|
316
|
+
address = agent_model['sfpAddress'].to_s.strip
|
317
|
+
port = agent_model['sfpPort'].to_s.strip
|
318
|
+
end
|
319
|
+
|
320
|
+
return false if address == '' or port == ''
|
321
|
+
|
322
|
+
name = agent_model['_self']
|
323
|
+
finder = Sfp::Helper::SchemaCollector.new
|
324
|
+
{:agent => agent_model}.accept(finder)
|
325
|
+
schemata = finder.schemata.uniq.map { |x| x.sub(/^\$\./, '').downcase }
|
326
|
+
|
327
|
+
begin
|
328
|
+
# get modules list
|
329
|
+
code, body = get_data(address, port, '/modules')
|
330
|
+
raise Exception, "Unable to get modules list from #{name}" if code.to_i != 200
|
331
|
+
|
332
|
+
modules = JSON[body]
|
333
|
+
list = ''
|
334
|
+
schemata.each { |m|
|
335
|
+
list += "#{m} " if File.exist?("#{@modules_dir}/#{m}") and
|
336
|
+
(not modules.has_key?(m) or modules[m] != get_local_module_hash(m).to_s)
|
337
|
+
}
|
338
|
+
|
339
|
+
return true if list == ''
|
340
|
+
|
341
|
+
if system("cd #{@modules_dir}; ./install_module #{address} #{port} #{list} 1>/dev/null 2>/tmp/install_module.error")
|
342
|
+
puts "Push modules #{list}to #{name} [OK]".green
|
343
|
+
else
|
344
|
+
puts "Push modules #{list}to #{name} [Failed]".red
|
345
|
+
end
|
346
|
+
|
347
|
+
return true
|
348
|
+
|
349
|
+
rescue Exception => e
|
350
|
+
puts "[WARN] Cannot push module to #{name} - #{e}".red
|
351
|
+
end
|
352
|
+
|
353
|
+
false
|
354
|
+
end
|
355
|
+
|
356
|
+
# return the list of Hash value of all modules
|
357
|
+
#
|
358
|
+
def get_local_module_hash(name)
|
359
|
+
module_dir = "#{@modules_dir}/#{name}"
|
360
|
+
if File.directory? module_dir
|
361
|
+
if `which md5sum`.strip.length > 0
|
362
|
+
return `find #{module_dir} -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum | awk '{print $1}'`.strip
|
363
|
+
elsif `which md5`.strip.length > 0
|
364
|
+
return `find #{module_dir} -type f -exec md5 {} + | awk '{print $4}' | sort | md5`.strip
|
365
|
+
end
|
366
|
+
end
|
367
|
+
nil
|
368
|
+
end
|
369
|
+
|
370
|
+
def get_node_state(name, do_push_modules=false)
|
371
|
+
push_modules(@model[name]) if do_push_modules
|
372
|
+
|
373
|
+
model = get_schemata
|
374
|
+
model[name] = @model[name]
|
375
|
+
|
376
|
+
begin
|
377
|
+
if http_send_agent_model(name, model)
|
378
|
+
agent_state = http_get_agent_state(name, model)
|
379
|
+
raise Exception, "Cannot get the current state of #{name}" if agent_state.nil?
|
380
|
+
return agent_state[name]
|
381
|
+
end
|
382
|
+
rescue Exception => e
|
383
|
+
puts "[WARN] Cannot get the current state of #{name} : #{e}".red
|
384
|
+
end
|
385
|
+
SfpUnknown
|
386
|
+
end
|
387
|
+
|
388
|
+
# send HTTP PUT request to push agent's model
|
389
|
+
#
|
390
|
+
def http_send_agent_model(name, model)
|
391
|
+
return false if !model[name].is_a?(Hash) or !model[name]['sfpAddress'].is_a?(String)
|
392
|
+
address = model[name]['sfpAddress'].to_s.strip
|
393
|
+
port = model[name]['sfpPort'].to_s.strip
|
394
|
+
if address != '' and port != ''
|
395
|
+
model = Sfp::Helper.deep_clone(model)
|
396
|
+
model.accept(ParentEliminator)
|
397
|
+
data = {'model' => JSON.generate(model)}
|
398
|
+
code, _ = put_data(address, port, '/model', data)
|
399
|
+
return (code.to_i == 200)
|
400
|
+
end
|
401
|
+
false
|
402
|
+
end
|
403
|
+
|
404
|
+
# send HTTP GET requst to get agent's state
|
405
|
+
#
|
406
|
+
def http_get_agent_state(name, model)
|
407
|
+
return nil if !model[name].is_a?(Hash) or !model[name]['sfpAddress'].is_a?(String)
|
408
|
+
address = model[name]['sfpAddress'].to_s.strip
|
409
|
+
port = model[name]['sfpPort'].to_s.strip
|
410
|
+
if address != '' and port != ''
|
411
|
+
code, body = get_data(address, port, '/sfpstate')
|
412
|
+
if code.to_i == 200 and body.length >= 2
|
413
|
+
state = JSON[body]
|
414
|
+
return state['state'] if state.is_a?(Hash)
|
415
|
+
return state
|
416
|
+
end
|
417
|
+
end
|
418
|
+
nil
|
419
|
+
end
|
420
|
+
|
421
|
+
def get_schemata
|
422
|
+
@model.select { |k,v| k[0,1] != '_' and v.is_a?(Hash) and
|
423
|
+
v['_context'] == 'class'
|
424
|
+
}
|
425
|
+
end
|
426
|
+
|
427
|
+
def get_agents
|
428
|
+
#@model.select { |k,v| !(k[0,1] == '_' or not v.is_a?(Hash) or
|
429
|
+
# v['_context'] != 'object' or v['_classes'].index(AgentSchema).nil?)
|
430
|
+
#}
|
431
|
+
Nuri::Master.agents(@model)
|
432
|
+
end
|
433
|
+
|
434
|
+
def self.agents(sfp)
|
435
|
+
sfp.select { |k,v| !(k[0] == '_' or not v.is_a?(Hash) or
|
436
|
+
v['_context'] != 'object' or v['_classes'].index(AgentSchema).nil?)
|
437
|
+
}
|
438
|
+
end
|
439
|
+
|
440
|
+
def get_vms
|
441
|
+
get_agents.select { |k,v| not v['_classes'].index(VMSchema).nil? }
|
442
|
+
end
|
443
|
+
|
444
|
+
def to_state(name, value)
|
445
|
+
raise Exception, 'Given value should be a Hash!' if not value.is_a?(Hash)
|
446
|
+
value['_self'] = name
|
447
|
+
value['_context'] = 'state'
|
448
|
+
value
|
449
|
+
end
|
450
|
+
|
451
|
+
def get_exist_vms
|
452
|
+
get_vms.select { |name,model| model['sfpAddress'].is_a?(String) and
|
453
|
+
model['sfpAddress'] != '' }
|
454
|
+
end
|
455
|
+
|
456
|
+
|
457
|
+
SfpUndefinedString = Sfp::Undefined.create('$.String')
|
458
|
+
SfpUndefinedNumber = Sfp::Undefined.create('$.Number')
|
459
|
+
SfpUndefinedBoolean = Sfp::Undefined.create('$.Boolean')
|
460
|
+
|
461
|
+
VisitorNotExistNodeState = Object.new
|
462
|
+
def VisitorNotExistNodeState.visit(name, value, parent)
|
463
|
+
return false if name[0,1] == '_'
|
464
|
+
if not value.is_a?(Hash)
|
465
|
+
if value.is_a?(String)
|
466
|
+
if value.isref
|
467
|
+
ref_value = parent.at?(value)
|
468
|
+
# TODO - need to handle a reference to a primitive value
|
469
|
+
if ref_value.is_a?(Hash) and (ref_value.isobject or ref_value.isnull)
|
470
|
+
parent[name] = Sfp::Undefined.create(ref_value['_isa'])
|
471
|
+
elsif ref_value.is_a?(Sfp::Undefined) or ref_value.is_a?(Sfp::Unknown)
|
472
|
+
parent[name] = ref_value
|
473
|
+
else
|
474
|
+
puts "[WARN] Sfp::Undefined => #{parent.ref.push(name)}: #{ref_value.class.name}"
|
475
|
+
parent[name] = SfpUndefined
|
476
|
+
end
|
477
|
+
else
|
478
|
+
parent[name] = SfpUndefinedString
|
479
|
+
end
|
480
|
+
elsif value.is_a?(Fixnum) or value.is_a?(Float)
|
481
|
+
parent[name] = SfpUndefinedNumber
|
482
|
+
elsif value.is_a?(TrueClass) or value.is_a?(FalseClass)
|
483
|
+
parent[name] = SfpUndefinedBoolean
|
484
|
+
else
|
485
|
+
puts "[WARN] Sfp::Undefined => " + parent.ref.push(name) + ": " + value.class.name
|
486
|
+
parent[name] = SfpUndefined
|
487
|
+
end
|
488
|
+
elsif value['_context'] == 'null' or value['_context'] == 'any_value'
|
489
|
+
parent[name] = Sfp::Undefined.create(value['_isa'])
|
490
|
+
elsif value['_context'] != 'object'
|
491
|
+
parent.delete(name)
|
492
|
+
end
|
493
|
+
true
|
494
|
+
end
|
495
|
+
|
496
|
+
VisitorDeadAgentNodeState = Object.new
|
497
|
+
def VisitorDeadAgentNodeState.visit(name, value, parent)
|
498
|
+
return false if name[0,1] == '_'
|
499
|
+
if not value.is_a?(Hash)
|
500
|
+
if value.is_a?(String)
|
501
|
+
if value.isref
|
502
|
+
ref_value = parent.at?(value)
|
503
|
+
# TODO - need to handle a reference to a primitive value
|
504
|
+
if ref_value.is_a?(Hash) and (ref_value.isobject or ref_value.isnull)
|
505
|
+
parent[name] = Sfp::Unknown.create(ref_value['_isa'])
|
506
|
+
elsif ref_value.is_a?(Sfp::Unknown) or ref_value.is_a?(Sfp::Unknown)
|
507
|
+
parent[name] = ref_value
|
508
|
+
else
|
509
|
+
puts "[WARN] Sfp::Unknown => #{parent.ref.push(name)}: #{ref_value.class.name}"
|
510
|
+
parent[name] = SfpUnknown
|
511
|
+
end
|
512
|
+
else
|
513
|
+
parent[name] = Sfp::Unknown.create('$.String')
|
514
|
+
end
|
515
|
+
elsif value.is_a?(Fixnum) or value.is_a?(Float)
|
516
|
+
parent[name] = Sfp::Unknown.create('$.Number')
|
517
|
+
elsif value.is_a?(TrueClass) or value.is_a?(FalseClass)
|
518
|
+
parent[name] = Sfp::Unknown.create('$.Boolean')
|
519
|
+
else
|
520
|
+
puts "[WARN] Sfp::Unknown => " + parent.ref.push(name) + ": " + value.class.name
|
521
|
+
parent[name] = SfpUnknown
|
522
|
+
end
|
523
|
+
elsif value['_context'] == 'null' or value['_context'] == 'any_value'
|
524
|
+
parent[name] = Sfp::Unknown.create(value['_isa'])
|
525
|
+
elsif value['_context'] != 'object'
|
526
|
+
parent.delete(name)
|
527
|
+
end
|
528
|
+
true
|
529
|
+
end
|
530
|
+
|
531
|
+
class VMProcedureModifier
|
532
|
+
def initialize(root)
|
533
|
+
@root = root
|
534
|
+
end
|
535
|
+
|
536
|
+
def visit(name, value, parent)
|
537
|
+
return false if name[0,1] == '_'
|
538
|
+
if value.is_a?(Hash) and value['_context'] == 'procedure'
|
539
|
+
_, agent, _ = parent.ref.split('.', 3)
|
540
|
+
if not @root[agent]['_classes'].index(VMSchema).nil?
|
541
|
+
value['_condition']["$.#{agent}.created"] = Sfp::Helper::Constraint.equals(true)
|
542
|
+
end
|
543
|
+
end
|
544
|
+
true
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
ParentEliminator = ::Sfp::Visitor::ParentEliminator.new
|
549
|
+
|
550
|
+
ParentGenerator = Object.new
|
551
|
+
def ParentGenerator.visit(name, value, parent)
|
552
|
+
value['_parent'] = parent if value.is_a?(Hash)
|
553
|
+
true
|
554
|
+
end
|
555
|
+
|
556
|
+
FinalAttributeRemover = Object.new
|
557
|
+
def FinalAttributeRemover.visit(name, value, parent)
|
558
|
+
if value.is_a?(Hash) and value.has_key?('_finals')
|
559
|
+
value['_finals'].each { |attr| value.delete(attr) }
|
560
|
+
end
|
561
|
+
true
|
562
|
+
end
|
563
|
+
|
564
|
+
SfpUnknownRemover = Object.new
|
565
|
+
def SfpUnknownRemover.visit(name, value, parent)
|
566
|
+
parent.delete(name) if value.is_a?(Sfp::Unknown)
|
567
|
+
true
|
568
|
+
end
|
569
|
+
|
570
|
+
SASPostProcessor = Object.new
|
571
|
+
# set a map of VM-name => VM-model
|
572
|
+
def SASPostProcessor.set_map(map)
|
573
|
+
@map = map
|
574
|
+
end
|
575
|
+
# post processing SAS after compilation
|
576
|
+
# goal: to add additional effects whenever a VM is deleted
|
577
|
+
def SASPostProcessor.sas_post_processor(parser)
|
578
|
+
return if parser.operators.nil?
|
579
|
+
parser.operators.each do |name, operator|
|
580
|
+
# skip if it's not "delete_vm"
|
581
|
+
next if !(name =~ /\.delete_vm$/) or !operator.params.has_key?('$.vm')
|
582
|
+
vm = operator.params['$.vm'].sub(/^\$\./, '')
|
583
|
+
next if !@map.has_key?(vm)
|
584
|
+
|
585
|
+
# for each not-exist state VM, add an effect
|
586
|
+
@map[vm].each { |k,v|
|
587
|
+
next if operator.has_key?(k) # skip if variable is exist (avoid overwrite)
|
588
|
+
next if k =~ /\.sfpAddress/ or k =~ /\.sfpPort/ # skip "sfpAddress" and "sfpPort"
|
589
|
+
# because these will be assigned dynamically
|
590
|
+
var = parser.variables[k]
|
591
|
+
next if var.nil? # the variable is not found
|
592
|
+
|
593
|
+
if v.is_a?(Hash)
|
594
|
+
val = parser.types[v['_value']][0] if v['_context'] == 'null'
|
595
|
+
raise Exception, "Not implemented yet." # this may arise on Set values
|
596
|
+
else
|
597
|
+
val = v
|
598
|
+
end
|
599
|
+
|
600
|
+
# add the value to variable's values
|
601
|
+
var << val
|
602
|
+
var.uniq!
|
603
|
+
|
604
|
+
# create new parameter, and then add to the operator
|
605
|
+
parameter = Sfp::Parameter.new(var, nil, val)
|
606
|
+
operator[var.name] = parameter
|
607
|
+
}
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
def self.start
|
612
|
+
# TODO
|
613
|
+
fork {
|
614
|
+
while true do
|
615
|
+
sleep 5000
|
616
|
+
end
|
617
|
+
}
|
618
|
+
end
|
619
|
+
|
620
|
+
def self.stop
|
621
|
+
# TODO
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
### Helper classes/modules
|
626
|
+
|
627
|
+
class Nuri::Master::GoalGenerator
|
628
|
+
attr_reader :results
|
629
|
+
|
630
|
+
def initialize
|
631
|
+
@results = Sfp::Helper::Constraint.and('goal')
|
632
|
+
end
|
633
|
+
|
634
|
+
def visit(name, value, parent)
|
635
|
+
return false if name[0,1] == '_'
|
636
|
+
|
637
|
+
if value.is_a?(Hash)
|
638
|
+
return true if value['_context'] == 'object'
|
639
|
+
|
640
|
+
if parent.has_key?('_finals') and parent['_finals'].index(name).nil?
|
641
|
+
if value['_context'] == 'set'
|
642
|
+
@results[parent.ref.push(name)] = Sfp::Helper::Constraint.equals(value['_values'])
|
643
|
+
elsif value['_context'] == 'null'
|
644
|
+
# HACK! This should not be commented => null value should not be ignored.
|
645
|
+
#@results[parent.ref.push(name)] = Sfp::Helper::Constraint.equals(value)
|
646
|
+
end
|
647
|
+
end
|
648
|
+
return false
|
649
|
+
end
|
650
|
+
if parent.has_key?('_finals') and parent['_finals'].index(name).nil?
|
651
|
+
@results[ parent.ref.push(name) ] = Sfp::Helper::Constraint.equals(value)
|
652
|
+
end
|
653
|
+
false
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
class Sfp::Helper::SfpFlatten
|
658
|
+
attr_reader :results
|
659
|
+
|
660
|
+
def initialize
|
661
|
+
@results = {}
|
662
|
+
end
|
663
|
+
|
664
|
+
def visit(name, value, parent)
|
665
|
+
return false if name[0,1] == '_'
|
666
|
+
if value.is_a?(Hash)
|
667
|
+
return true if value['_context'] == 'object'
|
668
|
+
|
669
|
+
@results[parent.ref.push(name)] = value if value['_context'] == 'null'
|
670
|
+
@results[parent.ref.push(name)] = value if value['_context'] == 'set'
|
671
|
+
|
672
|
+
return false
|
673
|
+
end
|
674
|
+
|
675
|
+
@results[parent.ref.push(name)] = value
|
676
|
+
false
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
module Sfp::Helper
|
681
|
+
def self.sfp_to_s(v)
|
682
|
+
if v.is_a?(Hash)
|
683
|
+
return "null" if v['_context'] == 'null'
|
684
|
+
return v['_values'].inspect if v['_context'] == 'set'
|
685
|
+
return "<hash>"
|
686
|
+
elsif v.is_a?(String) and v =~ /^\$\./
|
687
|
+
return v
|
688
|
+
end
|
689
|
+
v.inspect
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
class Sfp::Helper::SchemaCollector
|
694
|
+
attr_reader :schemata
|
695
|
+
def initialize
|
696
|
+
@schemata = []
|
697
|
+
end
|
698
|
+
|
699
|
+
def visit(name, value, parent)
|
700
|
+
if value.is_a?(Hash) and value.has_key?('_classes')
|
701
|
+
value['_classes'].each { |s| @schemata << s }
|
702
|
+
end
|
703
|
+
true
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
class Sfp::Helper::CloudFinder
|
708
|
+
CloudSchema = '$.Cloud'
|
709
|
+
attr_accessor :clouds
|
710
|
+
|
711
|
+
def reset
|
712
|
+
@clouds = []
|
713
|
+
self
|
714
|
+
end
|
715
|
+
|
716
|
+
def visit(name, value, parent)
|
717
|
+
if value.is_a?(Hash)
|
718
|
+
if value['_context'] == 'object'
|
719
|
+
@clouds << parent.ref.push(name) if value.has_key?('_classes') and value['_classes'].index(CloudSchema)
|
720
|
+
return true
|
721
|
+
end
|
722
|
+
end
|
723
|
+
false
|
724
|
+
end
|
725
|
+
end
|