nuri 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -1
- data/VERSION +1 -1
- data/bin/nuri +60 -14
- data/bin/nuri-install-module +17 -9
- data/examples/mockcloud/apache2.sfp +14 -0
- data/examples/mockcloud/ping.rb +38 -0
- data/examples/openstack/openstack-hadoop1-cluster.sfp +37 -0
- data/examples/openstack/openstack-hadoop2-cluster.sfp +39 -0
- data/examples/v2/apache.sfp +30 -0
- data/examples/v2/aptpackage.sfp +6 -0
- data/examples/v2/mock1.sfp +12 -0
- data/examples/v2/package.sfp +22 -0
- data/examples/v2/service.sfp +94 -0
- data/examples/v2/tarpackage.sfp +5 -0
- data/lib/nuri.rb +14 -10
- data/lib/nuri/choreographer.rb +3 -3
- data/lib/nuri/helper.rb +20 -10
- data/lib/nuri/master.rb +82 -54
- data/lib/nuri/orchestrator.rb +1 -1
- data/modules/.gitignore +0 -4
- data/modules/README.md +11 -0
- data/modules/apache/apache.sfp +2 -1
- data/modules/file/file.rb +49 -19
- data/modules/hadoop1/hadoop1.rb +18 -11
- data/modules/hadoop2/hadoop2.rb +11 -11
- data/modules/hadoop2/hadoop2.sfp +7 -6
- data/modules/hadoop2/yarn-site.xml +5 -0
- data/modules/machine/machine.rb +24 -14
- data/modules/openstack/README.md +24 -0
- data/modules/openstack/config.yml +5 -0
- data/modules/openstack/example.sfp +9 -0
- data/modules/openstack/openstack.rb +329 -0
- data/modules/openstack/openstack.sfp +24 -0
- data/modules/os/os.rb +1 -1
- data/modules/package2/apt-repo-list.sh +15 -0
- data/modules/package2/package2.rb +213 -43
- data/modules/package2/package2.sfp +3 -2
- data/modules/pyfile/README.md +4 -0
- data/modules/vm/vm.rb +3 -1
- data/modules/vm/vm.sfp +4 -3
- metadata +20 -3
- data/modules/hpcloud/test.sfp +0 -5
data/lib/nuri.rb
CHANGED
@@ -1,16 +1,11 @@
|
|
1
|
-
# external dependencies
|
2
|
-
require 'rubygems'
|
3
|
-
require 'json'
|
4
|
-
require 'sfplanner'
|
5
|
-
require 'colorize'
|
6
|
-
require "coderay"
|
7
|
-
require 'logger'
|
8
|
-
require 'yaml'
|
9
|
-
|
10
1
|
# define main module
|
11
2
|
module Nuri
|
12
3
|
def self.windows?
|
13
|
-
|
4
|
+
platform.include?("mswin")
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.platform
|
8
|
+
RUBY_PLATFORM.downcase
|
14
9
|
end
|
15
10
|
|
16
11
|
def self.home
|
@@ -52,6 +47,15 @@ module Nuri
|
|
52
47
|
end
|
53
48
|
end
|
54
49
|
|
50
|
+
# external dependencies
|
51
|
+
require 'rubygems'
|
52
|
+
require 'json'
|
53
|
+
require 'sfplanner'
|
54
|
+
require 'colorize'
|
55
|
+
require "coderay"
|
56
|
+
require 'logger'
|
57
|
+
require 'yaml'
|
58
|
+
|
55
59
|
# internal dependencies
|
56
60
|
libdir = File.dirname(__FILE__) << '/nuri'
|
57
61
|
['constraint_helper.rb', 'helper.rb', 'orchestrator.rb',
|
data/lib/nuri/choreographer.rb
CHANGED
@@ -35,10 +35,10 @@ module Nuri::Choreographer
|
|
35
35
|
# generate local BSig model
|
36
36
|
local_bsigs = {}
|
37
37
|
self.get_agents.each do |name,model|
|
38
|
-
|
38
|
+
namespace = model.ref + '.'
|
39
39
|
local = {'id' => bsig['id'], 'operators' => [], 'goal' => {}}
|
40
|
-
bsig['operators'].each { |op| local['operators'] << op if op['name'][0,
|
41
|
-
bsig['goal_operator'].each { |var,op_name| local['goal'][var] = bsig['goal'][var] if op_name[0,
|
40
|
+
bsig['operators'].each { |op| local['operators'] << op if op['name'][0, namespace.length] == namespace }
|
41
|
+
bsig['goal_operator'].each { |var,op_name| local['goal'][var] = bsig['goal'][var] if op_name[0, namespace.length] == namespace }
|
42
42
|
local_bsigs[name] = local
|
43
43
|
end
|
44
44
|
end
|
data/lib/nuri/helper.rb
CHANGED
@@ -42,27 +42,30 @@ module Sfp::Helper
|
|
42
42
|
end
|
43
43
|
|
44
44
|
module Nuri::Helper
|
45
|
-
|
45
|
+
DefaultHTTPOpenTimeout = 2
|
46
|
+
DefaultHTTPReadTimeout = 1800
|
47
|
+
|
48
|
+
def post_data(address, port, path, data, open_timeout=DefaultHTTPOpenTimeout, read_timeout=DefaultHTTPReadTimeout)
|
46
49
|
uri = create_uri(address, port, path)
|
47
50
|
req = Net::HTTP::Post.new(uri.path)
|
48
51
|
req.set_form_data(data)
|
49
52
|
http_request(uri, req, open_timeout, read_timeout)
|
50
53
|
end
|
51
54
|
|
52
|
-
def put_data(address, port, path, data, open_timeout=
|
55
|
+
def put_data(address, port, path, data, open_timeout=DefaultHTTPOpenTimeout, read_timeout=DefaultHTTPReadTimeout)
|
53
56
|
uri = create_uri(address, port, path)
|
54
57
|
req = Net::HTTP::Put.new(uri.path)
|
55
58
|
req.set_form_data(data)
|
56
59
|
http_request(uri, req, open_timeout, read_timeout)
|
57
60
|
end
|
58
61
|
|
59
|
-
def get_data(address, port, path, open_timeout=
|
62
|
+
def get_data(address, port, path, open_timeout=DefaultHTTPOpenTimeout, read_timeout=DefaultHTTPReadTimeout)
|
60
63
|
uri = create_uri(address, port, path)
|
61
64
|
req = Net::HTTP::Get.new(uri.path)
|
62
65
|
http_request(uri, req, open_timeout, read_timeout)
|
63
66
|
end
|
64
67
|
|
65
|
-
def delete_data(address, port, path, open_timeout=
|
68
|
+
def delete_data(address, port, path, open_timeout=DefaultHTTPOpenTimeout, read_timeout=DefaultHTTPReadTimeout)
|
66
69
|
uri = create_uri(address, port, path)
|
67
70
|
req = Net::HTTP::Delete.new(uri.path)
|
68
71
|
http_request(uri, req, open_timeout, read_timeout)
|
@@ -80,14 +83,21 @@ module Nuri::Helper
|
|
80
83
|
end
|
81
84
|
|
82
85
|
def use_http_proxy?(uri)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
86
|
+
parts = uri.host.split('.')
|
87
|
+
if parts[0] == '10' or
|
88
|
+
(parts[0] == '172' and parts[1] == '16') or
|
89
|
+
(parts[0] == '192' and parts[1] == '168')
|
90
|
+
false
|
91
|
+
else
|
92
|
+
ENV['no_proxy'].to_s.split(',').each { |pattern|
|
93
|
+
pattern.chop! if pattern[-1] == '*'
|
94
|
+
return false if uri.host[0,pattern.length] == pattern
|
95
|
+
}
|
96
|
+
true
|
97
|
+
end
|
88
98
|
end
|
89
99
|
|
90
|
-
def http_request(uri, request, open_timeout=
|
100
|
+
def http_request(uri, request, open_timeout=DefaultHTTPOpenTimeout, read_timeout=DefaultHTTPReadTimeout)
|
91
101
|
if ENV['http_proxy'].to_s.strip != '' and use_http_proxy?(uri)
|
92
102
|
proxy = URI.parse(ENV['http_proxy'])
|
93
103
|
http = Net::HTTP::Proxy(proxy.host, proxy.port).new(uri.host, uri.port)
|
data/lib/nuri/master.rb
CHANGED
@@ -94,6 +94,7 @@ class Nuri::Master
|
|
94
94
|
node_name = name
|
95
95
|
node_state = get_node_state(node_name, !!p[:push_modules])
|
96
96
|
mutex.synchronize { state[node_name] = node_state }
|
97
|
+
state[node_name] = node_state
|
97
98
|
}
|
98
99
|
end
|
99
100
|
total = agents.keys.length - vms.keys.length
|
@@ -110,6 +111,7 @@ class Nuri::Master
|
|
110
111
|
node_name = name
|
111
112
|
node_state = get_node_state(node_name, !!p[:push_modules])
|
112
113
|
mutex.synchronize { state[node_name] = node_state }
|
114
|
+
state[node_name] = node_state
|
113
115
|
}
|
114
116
|
}
|
115
117
|
|
@@ -124,28 +126,35 @@ class Nuri::Master
|
|
124
126
|
# update <vm>.in_cloud value
|
125
127
|
update_cloud_vm_relations(state, vms)
|
126
128
|
|
129
|
+
agents.merge!(exist_vms)
|
130
|
+
push_agents_list(agents, {:reset => true})
|
131
|
+
|
127
132
|
state
|
128
133
|
end
|
129
134
|
|
130
135
|
protected
|
131
136
|
def format_benchmark(benchmark)
|
132
|
-
|
137
|
+
user = (benchmark.utime + benchmark.cutime).round(3)
|
138
|
+
system = (benchmark.stime + benchmark.cstime).round(3)
|
139
|
+
real = benchmark.real.round(3)
|
140
|
+
"benchmark: user=#{user} sys=#{system} real=#{real}"
|
133
141
|
end
|
134
142
|
|
135
|
-
def create_plan_task(
|
143
|
+
def create_plan_task(opts={})
|
136
144
|
task = get_schemata
|
137
145
|
|
138
146
|
print "Getting current state "
|
139
|
-
puts (
|
147
|
+
puts (opts[:color] ? "[Wait]".yellow : "[Wait]")
|
140
148
|
|
141
|
-
|
142
|
-
task['initial'] = to_state('initial', get_state(
|
149
|
+
benchmark = Benchmark.measure do
|
150
|
+
task['initial'] = to_state('initial', get_state(opts))
|
143
151
|
end
|
152
|
+
#puts YAML.dump(task['initial'])
|
144
153
|
|
145
|
-
|
146
|
-
|
147
|
-
puts " " + format_benchmark(b)
|
154
|
+
puts "Getting current state " + (opts[:color] ? "[OK] ".green : "[OK] ") +
|
155
|
+
format_benchmark(benchmark)
|
148
156
|
|
157
|
+
task['initial'].accept(FinalAttributeRemover)
|
149
158
|
task['initial'].accept(Sfp::Visitor::SfpGenerator.new(task))
|
150
159
|
f1 = Sfp::Helper::SfpFlatten.new
|
151
160
|
task['initial'].accept(f1)
|
@@ -162,26 +171,26 @@ class Nuri::Master
|
|
162
171
|
task['goal'] = goalgen.results
|
163
172
|
|
164
173
|
# find dead-node, remove from the task, print WARNING to the console
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
puts "
|
171
|
-
|
174
|
+
init = task['initial']
|
175
|
+
init.keys.each do |name|
|
176
|
+
next if not init[name].is_a?(Sfp::Unknown)
|
177
|
+
init.delete(name)
|
178
|
+
task['goal'].keep_if { |var,val| !(var =~ /(\$\.#{name}\.|\$\.#{name}$)/) }
|
179
|
+
puts (opts[:color] ? "[Warn]".red : "[Warn]") + " Remove node #{name} from the planning task."
|
180
|
+
end
|
172
181
|
|
173
182
|
# print the status of goal state
|
174
183
|
puts "Goal state:"
|
175
|
-
goalgen.results.each { |
|
176
|
-
next if
|
184
|
+
goalgen.results.each { |var,val|
|
185
|
+
next if var[0] == '_'
|
177
186
|
|
178
|
-
print " #{
|
179
|
-
value = Sfp::Helper::Sfp2Ruby.val(
|
180
|
-
print (
|
187
|
+
print " #{var}: "
|
188
|
+
value = Sfp::Helper::Sfp2Ruby.val(val['_value']).to_s
|
189
|
+
print (opts[:color] ? value.green : value)
|
181
190
|
|
182
|
-
if f1.results.has_key?(
|
183
|
-
value = Sfp::Helper::Sfp2Ruby.val(f1.results[
|
184
|
-
print (
|
191
|
+
if f1.results.has_key?(var) and f1.results[var] != val['_value']
|
192
|
+
value = Sfp::Helper::Sfp2Ruby.val(f1.results[var]).to_s
|
193
|
+
print " ( " + (opts[:color] ? value.red : value) + " )"
|
185
194
|
end
|
186
195
|
|
187
196
|
puts ""
|
@@ -232,12 +241,13 @@ class Nuri::Master
|
|
232
241
|
end
|
233
242
|
|
234
243
|
def get_not_exist_vm_state(model)
|
235
|
-
|
244
|
+
state = Sfp::Helper.deep_clone(model)
|
245
|
+
s = {'state' => state}
|
236
246
|
s.accept(VisitorNotExistNodeState)
|
237
247
|
s.accept(ParentEliminator)
|
238
|
-
|
239
|
-
|
240
|
-
|
248
|
+
state['created'] = false
|
249
|
+
state['in_cloud'] = {'_context' => 'null', '_value' => CloudSchema}
|
250
|
+
state
|
241
251
|
end
|
242
252
|
|
243
253
|
def update_cloud_vm_relations(state, vms)
|
@@ -250,6 +260,7 @@ class Nuri::Master
|
|
250
260
|
next if not vms.has_key?(name)
|
251
261
|
if state[name].is_a?(Hash)
|
252
262
|
state[name]['in_cloud'] = cloud
|
263
|
+
state[name]['created'] = true
|
253
264
|
elsif state[name].is_a?(Sfp::Unknown)
|
254
265
|
state[name] = get_dead_vm_state(vms[name], cloud)
|
255
266
|
end
|
@@ -310,26 +321,33 @@ class Nuri::Master
|
|
310
321
|
true
|
311
322
|
end
|
312
323
|
|
313
|
-
def push_agents_list
|
324
|
+
def push_agents_list(agents=nil, options={})
|
314
325
|
begin
|
315
|
-
agents
|
326
|
+
agents ||= get_agents
|
327
|
+
data = {}
|
316
328
|
# generate agents list
|
317
329
|
get_agents.each do |name, model|
|
318
330
|
next if not model['sfpAddress'].is_a?(String)
|
319
331
|
address = model['sfpAddress'].to_s.strip
|
320
332
|
port = model['sfpPort'].to_s.strip.to_i
|
321
333
|
next if address == '' or port <= 0
|
322
|
-
|
334
|
+
data[name] = {:sfpAddress => address, :sfpPort => port}
|
323
335
|
end
|
324
|
-
|
336
|
+
json = {'agents' => JSON.generate(data)}
|
325
337
|
|
326
338
|
# send the list to all agents
|
327
|
-
|
328
|
-
|
339
|
+
data.each do |name, agent|
|
340
|
+
if options[:reset]
|
341
|
+
delete_data(agent[:sfpAddress], agent[:sfpPort], '/agents')
|
342
|
+
end
|
343
|
+
|
344
|
+
# update current agents list
|
345
|
+
code, _ = put_data(agent[:sfpAddress], agent[:sfpPort], '/agents', json, 5, 20)
|
329
346
|
raise Exception, "Push agents list to #{agent[:sfpAddress]}:#{agent[:sfpPort]} [Failed]" if code.to_i != 200
|
330
347
|
end
|
331
348
|
return true
|
332
349
|
rescue Exception => exp
|
350
|
+
#$stderr.puts "#{exp}\n#{exp.backtrace.join("\n")}"
|
333
351
|
end
|
334
352
|
false
|
335
353
|
end
|
@@ -359,9 +377,11 @@ class Nuri::Master
|
|
359
377
|
|
360
378
|
begin
|
361
379
|
# get modules list
|
362
|
-
code, body = get_data(address, port, '/modules')
|
380
|
+
code, body = get_data(address, port, '/modules', DefaultHTTPOpenTimeout, 5)
|
363
381
|
raise Exception, "Unable to get modules list from #{name}" if code.to_i != 200
|
364
382
|
|
383
|
+
#puts "#{name}'s modules: #{body}"
|
384
|
+
|
365
385
|
modules = JSON[body]
|
366
386
|
tobe_installed_modules = []
|
367
387
|
schemata.each do |name|
|
@@ -376,7 +396,9 @@ class Nuri::Master
|
|
376
396
|
|
377
397
|
### install new modules and replace old ones
|
378
398
|
list = tobe_installed_modules.join(" ")
|
379
|
-
|
399
|
+
cmd = "#{InstallModule} #{address}:#{port} #{list}"
|
400
|
+
#puts cmd
|
401
|
+
output = JSON.parse(`cd #{@modules_dir}; #{cmd}`)
|
380
402
|
if output['installed_modules'].length > 0
|
381
403
|
puts ("Push modules: " + output['installed_modules'].join(" ") + " to agent #{name} [OK]").green
|
382
404
|
end
|
@@ -435,7 +457,7 @@ class Nuri::Master
|
|
435
457
|
model = Sfp::Helper.deep_clone(model)
|
436
458
|
model.accept(ParentEliminator)
|
437
459
|
data = {'model' => JSON.generate(model)}
|
438
|
-
code, _ = put_data(address, port, '/model', data)
|
460
|
+
code, _ = put_data(address, port, '/model', data, DefaultHTTPOpenTimeout, 5)
|
439
461
|
return (code.to_i == 200)
|
440
462
|
end
|
441
463
|
false
|
@@ -448,7 +470,7 @@ class Nuri::Master
|
|
448
470
|
address = model[name]['sfpAddress'].to_s.strip
|
449
471
|
port = model[name]['sfpPort'].to_s.strip
|
450
472
|
if address != '' and port != ''
|
451
|
-
code, body = get_data(address, port, '/sfpstate')
|
473
|
+
code, body = get_data(address, port, '/sfpstate', DefaultHTTPOpenTimeout, 20)
|
452
474
|
if code.to_i == 200 and body.length >= 2
|
453
475
|
state = JSON[body]
|
454
476
|
return state['state'] if state.is_a?(Hash)
|
@@ -464,12 +486,9 @@ class Nuri::Master
|
|
464
486
|
}
|
465
487
|
end
|
466
488
|
|
467
|
-
def get_agents
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
def self.agents(sfp)
|
472
|
-
sfp.select { |k,v| !(k[0] == '_' or not v.is_a?(Hash) or
|
489
|
+
def get_agents(model=nil)
|
490
|
+
model ||= @model
|
491
|
+
model.select { |k,v| !(k[0] == '_' or not v.is_a?(Hash) or
|
473
492
|
v['_context'] != 'object' or v['_classes'].index(AgentSchema).nil?)
|
474
493
|
}
|
475
494
|
end
|
@@ -497,8 +516,18 @@ class Nuri::Master
|
|
497
516
|
|
498
517
|
VisitorNotExistNodeState = Object.new
|
499
518
|
def VisitorNotExistNodeState.visit(name, value, parent)
|
519
|
+
parent.delete(name) if name == '_parent'
|
500
520
|
return false if name[0,1] == '_'
|
501
|
-
if
|
521
|
+
if value.is_a?(Hash)
|
522
|
+
case value['_context']
|
523
|
+
when 'null', 'any_value'
|
524
|
+
parent[name] = Sfp::Undefined.create(value['_isa'])
|
525
|
+
when 'object', 'procedure'
|
526
|
+
# do nothing
|
527
|
+
else
|
528
|
+
parent.delete(name)
|
529
|
+
end
|
530
|
+
else
|
502
531
|
if value.is_a?(String)
|
503
532
|
if value.isref
|
504
533
|
ref_value = parent.at?(value)
|
@@ -508,7 +537,7 @@ class Nuri::Master
|
|
508
537
|
elsif ref_value.is_a?(Sfp::Undefined) or ref_value.is_a?(Sfp::Unknown)
|
509
538
|
parent[name] = ref_value
|
510
539
|
else
|
511
|
-
puts "[WARN] Sfp::Undefined => #{parent.ref.push(name)}: #{ref_value.class.name}"
|
540
|
+
#puts "[WARN] Sfp::Undefined => #{parent.ref.push(name)}: #{ref_value.class.name}"
|
512
541
|
parent[name] = SfpUndefined
|
513
542
|
end
|
514
543
|
else
|
@@ -519,13 +548,9 @@ class Nuri::Master
|
|
519
548
|
elsif value.is_a?(TrueClass) or value.is_a?(FalseClass)
|
520
549
|
parent[name] = SfpUndefinedBoolean
|
521
550
|
else
|
522
|
-
puts "[WARN] Sfp::Undefined => " + parent.ref.push(name) + ": " + value.class.name
|
551
|
+
#puts "[WARN] Sfp::Undefined => " + parent.ref.push(name) + ": " + value.class.name
|
523
552
|
parent[name] = SfpUndefined
|
524
553
|
end
|
525
|
-
elsif value['_context'] == 'null' or value['_context'] == 'any_value'
|
526
|
-
parent[name] = Sfp::Undefined.create(value['_isa'])
|
527
|
-
elsif value['_context'] != 'object'
|
528
|
-
parent.delete(name)
|
529
554
|
end
|
530
555
|
true
|
531
556
|
end
|
@@ -543,7 +568,7 @@ class Nuri::Master
|
|
543
568
|
elsif ref_value.is_a?(Sfp::Unknown) or ref_value.is_a?(Sfp::Unknown)
|
544
569
|
parent[name] = ref_value
|
545
570
|
else
|
546
|
-
puts "[WARN] Sfp::Unknown => #{parent.ref.push(name)}: #{ref_value.class.name}"
|
571
|
+
#puts "[WARN] Sfp::Unknown => #{parent.ref.push(name)}: #{ref_value.class.name}"
|
547
572
|
parent[name] = SfpUnknown
|
548
573
|
end
|
549
574
|
else
|
@@ -554,7 +579,7 @@ class Nuri::Master
|
|
554
579
|
elsif value.is_a?(TrueClass) or value.is_a?(FalseClass)
|
555
580
|
parent[name] = Sfp::Unknown.create('$.Boolean')
|
556
581
|
else
|
557
|
-
puts "[WARN] Sfp::Unknown => " + parent.ref.push(name) + ": " + value.class.name
|
582
|
+
#puts "[WARN] Sfp::Unknown => " + parent.ref.push(name) + ": " + value.class.name
|
558
583
|
parent[name] = SfpUnknown
|
559
584
|
end
|
560
585
|
elsif value['_context'] == 'null' or value['_context'] == 'any_value'
|
@@ -628,8 +653,11 @@ class Nuri::Master
|
|
628
653
|
next if var.nil? # the variable is not found
|
629
654
|
|
630
655
|
if v.is_a?(Hash)
|
631
|
-
|
632
|
-
|
656
|
+
if v['_context'] == 'null'
|
657
|
+
val = parser.types[v['_value']][0]
|
658
|
+
else
|
659
|
+
raise Exception, "Not implemented yet." # this may arise on Set values
|
660
|
+
end
|
633
661
|
else
|
634
662
|
val = v
|
635
663
|
end
|
data/lib/nuri/orchestrator.rb
CHANGED
@@ -164,7 +164,7 @@ module Nuri::Orchestrator
|
|
164
164
|
success = execute_action(action, options)
|
165
165
|
rescue Exception => exp
|
166
166
|
puts "Executing(#{i}) #{action[:string]} - thread ##{tid} " + (options[:color] ? "[Failed]".red : "[Failed]")
|
167
|
-
puts "#{exp}
|
167
|
+
puts "#{exp}" #\n#{exp.backtrace.join("\n")}"
|
168
168
|
end
|
169
169
|
break if success
|
170
170
|
end
|
data/modules/.gitignore
CHANGED
data/modules/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
This directory contains the modules that control your resources. Each directory contains a module. You can add your own module by creating a new directory and required files.
|
2
|
+
|
3
|
+
There are two types of module:
|
4
|
+
1. Native module - a module that is implemented in Ruby; it has two main files:
|
5
|
+
- `<module-name>.sfp` - contains the model of the resource in SFP language,
|
6
|
+
- `<module-name>.rb` - the implementation in Ruby.
|
7
|
+
2. Non-native module - a module that is implemented in other language (e.g. `pyfile`); it has two main files:
|
8
|
+
- `<module-name>.sfp` - contains the model of the resource in SFP language,
|
9
|
+
- `main` - the implementation in any programming language (must be executable through console).
|
10
|
+
|
11
|
+
You can put any necessary file to the directory. All files of the module's directory will be automatically pushed to the target agent by Nuri during deployment. Nuri will intelligently check any changes, and push the latest version.
|
data/modules/apache/apache.sfp
CHANGED
@@ -15,7 +15,8 @@ schema Apache extends Service {
|
|
15
15
|
// load balancer config
|
16
16
|
is_load_balancer : Bool
|
17
17
|
lb_members isset Node
|
18
|
-
|
18
|
+
// byrequests, bytraffic, bybusyness
|
19
|
+
lb_method : String
|
19
20
|
|
20
21
|
sub set_port(target isref Integer) {
|
21
22
|
condition {
|