nuri 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -1
  3. data/VERSION +1 -1
  4. data/bin/nuri +60 -14
  5. data/bin/nuri-install-module +17 -9
  6. data/examples/mockcloud/apache2.sfp +14 -0
  7. data/examples/mockcloud/ping.rb +38 -0
  8. data/examples/openstack/openstack-hadoop1-cluster.sfp +37 -0
  9. data/examples/openstack/openstack-hadoop2-cluster.sfp +39 -0
  10. data/examples/v2/apache.sfp +30 -0
  11. data/examples/v2/aptpackage.sfp +6 -0
  12. data/examples/v2/mock1.sfp +12 -0
  13. data/examples/v2/package.sfp +22 -0
  14. data/examples/v2/service.sfp +94 -0
  15. data/examples/v2/tarpackage.sfp +5 -0
  16. data/lib/nuri.rb +14 -10
  17. data/lib/nuri/choreographer.rb +3 -3
  18. data/lib/nuri/helper.rb +20 -10
  19. data/lib/nuri/master.rb +82 -54
  20. data/lib/nuri/orchestrator.rb +1 -1
  21. data/modules/.gitignore +0 -4
  22. data/modules/README.md +11 -0
  23. data/modules/apache/apache.sfp +2 -1
  24. data/modules/file/file.rb +49 -19
  25. data/modules/hadoop1/hadoop1.rb +18 -11
  26. data/modules/hadoop2/hadoop2.rb +11 -11
  27. data/modules/hadoop2/hadoop2.sfp +7 -6
  28. data/modules/hadoop2/yarn-site.xml +5 -0
  29. data/modules/machine/machine.rb +24 -14
  30. data/modules/openstack/README.md +24 -0
  31. data/modules/openstack/config.yml +5 -0
  32. data/modules/openstack/example.sfp +9 -0
  33. data/modules/openstack/openstack.rb +329 -0
  34. data/modules/openstack/openstack.sfp +24 -0
  35. data/modules/os/os.rb +1 -1
  36. data/modules/package2/apt-repo-list.sh +15 -0
  37. data/modules/package2/package2.rb +213 -43
  38. data/modules/package2/package2.sfp +3 -2
  39. data/modules/pyfile/README.md +4 -0
  40. data/modules/vm/vm.rb +3 -1
  41. data/modules/vm/vm.sfp +4 -3
  42. metadata +20 -3
  43. data/modules/hpcloud/test.sfp +0 -5
@@ -0,0 +1,5 @@
1
+ include "package.sfp"
2
+
3
+ schema TarPackage extends Package {
4
+ final home = "/opt/local"
5
+ }
@@ -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
- RUBY_PLATFORM.downcase.include?("mswin")
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',
@@ -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
- ref = model.ref
38
+ namespace = model.ref + '.'
39
39
  local = {'id' => bsig['id'], 'operators' => [], 'goal' => {}}
40
- bsig['operators'].each { |op| local['operators'] << op if op['name'][0, ref.length] == ref }
41
- bsig['goal_operator'].each { |var,op_name| local['goal'][var] = bsig['goal'][var] if op_name[0, ref.length] == ref }
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
@@ -42,27 +42,30 @@ module Sfp::Helper
42
42
  end
43
43
 
44
44
  module Nuri::Helper
45
- def post_data(address, port, path, data, open_timeout=5, read_timeout=1800)
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=5, read_timeout=1800)
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=5, read_timeout=1800)
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=5, read_timeout=1800)
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
- ENV['no_proxy'].to_s.split(',').each { |pattern|
84
- pattern.chop! if pattern[-1] == '*'
85
- return false if uri.host[0,pattern.length] == pattern
86
- }
87
- true
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=5, read_timeout=1800)
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)
@@ -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
- "cpu-time: user=#{benchmark.cutime.round(2)} sys=#{benchmark.cstime.round(2)} total=#{benchmark.total.round(2)}"
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(p={})
143
+ def create_plan_task(opts={})
136
144
  task = get_schemata
137
145
 
138
146
  print "Getting current state "
139
- puts (p[:color] ? "[Wait]".yellow : "[Wait]")
147
+ puts (opts[:color] ? "[Wait]".yellow : "[Wait]")
140
148
 
141
- b = Benchmark.measure do
142
- task['initial'] = to_state('initial', get_state(p))
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
- print "Getting current state "
146
- print (p[:color] ? "[OK]".green : "[OK]")
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
- dead_nodes = task['initial'].select { |k,v| v.is_a?(Sfp::Unknown) }
166
- dead_nodes.each_key { |name|
167
- task['initial'].delete(name)
168
- task['goal'].keep_if { |k,v| !(k =~ /(\$\.#{name}\.|\$\.#{name}$)/) }
169
- print (p[:color] ? "[Warn]".red : "[Warn]")
170
- puts " Removing node #{name} from the task."
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 { |k,v|
176
- next if k[0,1] == '_'
184
+ goalgen.results.each { |var,val|
185
+ next if var[0] == '_'
177
186
 
178
- print " #{k}: "
179
- value = Sfp::Helper::Sfp2Ruby.val(v['_value']).to_s
180
- print (p[:color] ? value.green : value) + " "
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?(k) and f1.results[k] != v['_value']
183
- value = Sfp::Helper::Sfp2Ruby.val(f1.results[k]).to_s
184
- print (p[:color] ? value.red : value)
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
- s = {'state' => Sfp::Helper.deep_clone(model)}
244
+ state = Sfp::Helper.deep_clone(model)
245
+ s = {'state' => state}
236
246
  s.accept(VisitorNotExistNodeState)
237
247
  s.accept(ParentEliminator)
238
- s['state']['created'] = false
239
- s['state']['in_cloud'] = {'_context' => 'null', '_value' => CloudSchema}
240
- s['state']
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
- agents[name] = {:sfpAddress => address, :sfpPort => port}
334
+ data[name] = {:sfpAddress => address, :sfpPort => port}
323
335
  end
324
- data = {'agents' => JSON.generate(agents)}
336
+ json = {'agents' => JSON.generate(data)}
325
337
 
326
338
  # send the list to all agents
327
- agents.each do |name, agent|
328
- code, _ = put_data(agent[:sfpAddress], agent[:sfpPort], '/agents', data, 5, 20)
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
- output = JSON.parse(`cd #{@modules_dir}; #{InstallModule} #{address}:#{port} #{list}`)
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
- Nuri::Master.agents(@model)
469
- end
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 not value.is_a?(Hash)
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
- val = parser.types[v['_value']][0] if v['_context'] == 'null'
632
- raise Exception, "Not implemented yet." # this may arise on Set values
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
@@ -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}\n#{exp.backtrace.join("\n")}"
167
+ puts "#{exp}" #\n#{exp.backtrace.join("\n")}"
168
168
  end
169
169
  break if success
170
170
  end
@@ -1,4 +0,0 @@
1
- *.tgz
2
- cell
3
- autonomy*
4
- idol*
@@ -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.
@@ -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
- lb_method : String // byrequests, bytraffic, bybusyness
18
+ // byrequests, bytraffic, bybusyness
19
+ lb_method : String
19
20
 
20
21
  sub set_port(target isref Integer) {
21
22
  condition {