orchestrator 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/orchestrator/api/dependencies_controller.rb +2 -1
  3. data/app/controllers/orchestrator/api/logs_controller.rb +37 -0
  4. data/app/controllers/orchestrator/api/systems_controller.rb +14 -12
  5. data/app/controllers/orchestrator/api/users_controller.rb +76 -0
  6. data/app/controllers/orchestrator/api/zones_controller.rb +2 -1
  7. data/app/controllers/orchestrator/api_controller.rb +2 -1
  8. data/app/controllers/orchestrator/base.rb +24 -7
  9. data/app/models/orchestrator/access_log.rb +10 -4
  10. data/app/models/orchestrator/edge_control.rb +25 -0
  11. data/app/models/user.rb +8 -0
  12. data/config/routes.rb +4 -0
  13. data/lib/orchestrator/control.rb +24 -3
  14. data/lib/orchestrator/core/mixin.rb +10 -4
  15. data/lib/orchestrator/core/module_manager.rb +3 -3
  16. data/lib/orchestrator/core/request_proxy.rb +24 -2
  17. data/lib/orchestrator/core/requests_proxy.rb +57 -3
  18. data/lib/orchestrator/core/system_proxy.rb +12 -6
  19. data/lib/orchestrator/device/processor.rb +7 -0
  20. data/lib/orchestrator/engine.rb +1 -0
  21. data/lib/orchestrator/logger.rb +2 -1
  22. data/lib/orchestrator/logic/manager.rb +2 -2
  23. data/lib/orchestrator/logic/mixin.rb +1 -1
  24. data/lib/orchestrator/remote/edge.rb +30 -0
  25. data/lib/orchestrator/remote/master.rb +150 -0
  26. data/lib/orchestrator/remote/proxy.rb +24 -0
  27. data/lib/orchestrator/status.rb +39 -4
  28. data/lib/orchestrator/system.rb +8 -0
  29. data/lib/orchestrator/utilities/constants.rb +4 -2
  30. data/lib/orchestrator/utilities/transcoder.rb +6 -2
  31. data/lib/orchestrator/version.rb +1 -1
  32. data/lib/orchestrator/websocket_manager.rb +121 -31
  33. metadata +10 -3
@@ -0,0 +1,24 @@
1
+ require 'set'
2
+
3
+
4
+ module Orchestrator
5
+ class Proxy
6
+ COMMANDS = Set.new([:exec, :bind, :unbind, :debug, :ignore])
7
+
8
+ def initialize(thread)
9
+ @thread = thread
10
+
11
+ @accept_connection = method :accept_connection
12
+ @new_connection = method :new_connection
13
+ @bind_error = method :bind_error
14
+
15
+ @shutdown = true
16
+ @edge_nodes = ::ThreadSafe::Cache.new # id => connection
17
+ @req_map = {} # connection => ::Set.new (defers)
18
+ @req_map = {}
19
+
20
+ @signal_bind = @thread.async method(:bind_actual)
21
+ @signal_unbind = @thread.async method(:unbind_actual)
22
+ end
23
+ end
24
+ end
@@ -97,12 +97,42 @@ module Orchestrator
97
97
  end
98
98
  end
99
99
 
100
- # TODO:: we also need the system class to contact each of the threads
101
- def reloaded_system(sys_id)
100
+ # Used to maintain subscriptions where module is moved to another thread
101
+ # or even another server.
102
+ def move(mod_id, to_thread)
103
+ return if to_thread == self
104
+
105
+ @thread.schedule do
106
+ subs = @subscriptions.delete(mod_id)
107
+ if subs
108
+ # Remove the system references
109
+ subs.each do |sub|
110
+ @systems[sub.sys_id].delete(sub) if sub.sys_id
111
+ end
112
+
113
+ # Transfer the subscriptions
114
+ to_thread.transfer(mod_id, subs)
115
+ end
116
+ end
117
+ end
118
+
119
+ def transfer(mod_id, subs)
120
+ @thread.schedule do
121
+ @subscriptions[mod_id] = subs
122
+
123
+ subs.each do |sub|
124
+ if sub.sys_id
125
+ @systems[sub.sys_id] ||= Set.new
126
+ @systems[sub.sys_id] << sub
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ # The System class contacts each of the threads to let them know of an update
133
+ def reloaded_system(sys_id, sys)
102
134
  subscriptions = @systems[sys_id]
103
135
  if subscriptions
104
- sys = ::Orchestrator::System.get(@system)
105
-
106
136
  subscriptions.each do |sub|
107
137
  old_id = sub.mod_id
108
138
 
@@ -125,6 +155,11 @@ module Orchestrator
125
155
  sub.notify(value) unless value.nil?
126
156
  end
127
157
 
158
+ # Transfer the subscription if on a different thread
159
+ if mod.thread != @thread
160
+ move(sub.mod_id.to_sym, mod.thread)
161
+ end
162
+
128
163
  # Perform any required cleanup
129
164
  if @subscriptions[old_id][sub.status].empty?
130
165
  @subscriptions[old_id].delete(sub.status)
@@ -43,6 +43,14 @@ module Orchestrator
43
43
  zone = zones[zone_id]
44
44
  @zones << zone unless zone.nil?
45
45
  end
46
+
47
+ # Inform status tracker that that the system has reloaded
48
+ # There may have been a change in module order etc
49
+ @controller.threads.each do |thread|
50
+ thread.next_tick do
51
+ thread.observer.reloaded_system(@config.id, self)
52
+ end
53
+ end
46
54
  end
47
55
 
48
56
  def get(mod, index)
@@ -15,13 +15,15 @@ module Orchestrator
15
15
  :on, :On, 'on', 'On',
16
16
  :yes, :Yes, 'yes', 'Yes',
17
17
  'down', 'Down', :down, :Down,
18
- 'open', 'Open', :open, :Open])
18
+ 'open', 'Open', :open, :Open,
19
+ 'active', 'Active', :active, :Active])
19
20
  Off_vars = Set.new([0, false, 'false', 'False',
20
21
  :off, :Off, 'off', 'Off',
21
22
  :no, :No, 'no', 'No',
22
23
  'up', 'Up', :up, :Up,
23
24
  'close', 'Close', :close, :Close,
24
- 'short', 'Short', :short, :Short])
25
+ 'short', 'Short', :short, :Short,
26
+ 'inactive', 'Inactive', :inactive, :Inactive])
25
27
 
26
28
 
27
29
  def in_range(num, max, min = 0)
@@ -12,9 +12,9 @@ module Orchestrator
12
12
  data.prepend('0') if data.length % 2 > 0
13
13
 
14
14
  # Breaks string into an array of characters
15
- output = ""
15
+ output = []
16
16
  data.scan(/.{2}/) { |byte| output << byte.hex}
17
- return output
17
+ output.pack('c*')
18
18
  end
19
19
 
20
20
  # Converts a binary string into a hex encoded string
@@ -22,6 +22,8 @@ module Orchestrator
22
22
  # @param data [String] a binary string
23
23
  # @return [String]
24
24
  def byte_to_hex(data)
25
+ data = array_to_str(data) if data.is_a? Array
26
+
25
27
  output = ""
26
28
  data.each_byte { |c|
27
29
  s = c.to_s(16)
@@ -36,6 +38,7 @@ module Orchestrator
36
38
  # @param data [String] data to be converted to bytes
37
39
  # @return [Array]
38
40
  def str_to_array(data)
41
+ return data if data.is_a? Array
39
42
  data.bytes.to_a
40
43
  end
41
44
 
@@ -44,6 +47,7 @@ module Orchestrator
44
47
  # @param data [Array] an array of bytes
45
48
  # @return [String]
46
49
  def array_to_str(data)
50
+ return data if data.is_a? String
47
51
  data.pack('c*')
48
52
  end
49
53
 
@@ -1,3 +1,3 @@
1
1
  module Orchestrator
2
- VERSION = "1.0.2"
2
+ VERSION = "1.0.3"
3
3
  end
@@ -83,6 +83,9 @@ module Orchestrator
83
83
  if COMMANDS.include?(cmd)
84
84
  @accessed << params[:sys] # Log the access
85
85
  self.__send__(cmd, params) # Execute the request
86
+
87
+ # Start logging
88
+ periodicly_update_logs if @accessTimer.nil?
86
89
  else
87
90
  @access_log.suspected = true
88
91
  @logger.warn("websocket requested unknown command '#{params[:cmd]}'")
@@ -95,7 +98,7 @@ module Orchestrator
95
98
  end
96
99
 
97
100
  # Raise an error if access is not granted
98
- result.catch do |err|
101
+ result.catch do |e|
99
102
  @access_log.suspected = true
100
103
  @logger.print_error(e, 'security check failed for websocket request')
101
104
  error_response(params[:id], ERRORS[:access_denied], e.message)
@@ -111,7 +114,7 @@ module Orchestrator
111
114
 
112
115
  def check_requirements(params)
113
116
  REQUIRED.each do |key|
114
- return false if params[key].nil?
117
+ return false unless params.has_key?(key)
115
118
  end
116
119
  true
117
120
  end
@@ -138,7 +141,7 @@ module Orchestrator
138
141
  if system
139
142
  mod_man = system.get(mod, index - 1)
140
143
  if mod_man
141
- req = Core::RequestProxy.new(@loop, mod_man)
144
+ req = Core::RequestProxy.new(@loop, mod_man, @user)
142
145
  result = req.send(name, *args)
143
146
  result.then(proc { |res|
144
147
  output = nil
@@ -310,8 +313,10 @@ module Orchestrator
310
313
  def debug(params)
311
314
  id = params[:id]
312
315
  sys = params[:sys]
313
- mod_s = params[:mod]
314
- mod = mod_s.to_sym if mod_s
316
+ mod = params[:mod].to_sym
317
+ index_s = params[:index]
318
+ index = nil
319
+ index = index_s.to_i if index_s
315
320
 
316
321
  if @debug.nil?
317
322
  @debug = @loop.defer
@@ -319,35 +324,82 @@ module Orchestrator
319
324
  @debug.promise.progress method(:debug_update)
320
325
  end
321
326
 
322
- # Set mod to get module level errors
323
- if mod && !@inspecting.include?(mod)
324
- mod_man = ::Orchestrator::Control.instance.loaded?(mod)
325
- if mod_man
326
- log = mod_man.logger
327
- log.add @debug
328
- log.level = :debug
329
- @inspecting.add mod
330
-
331
- # Set sys to get errors occurring outside of the modules
332
- if !@inspecting.include?(:self)
333
- @logger.add @debug
334
- @logger.level = :debug
335
- @inspecting.add :self
327
+ if index
328
+ # Look up the module ID on the thread pool
329
+ @loop.work(proc {
330
+ system = ::Orchestrator::System.get(sys)
331
+ if system
332
+ mod_man = system.get(mod, index - 1)
333
+ if mod_man
334
+ mod_man.settings.id
335
+ else
336
+ ::Libuv::Q.reject(@loop, 'debug failed: module #{sys}->#{mod}_#{index} not found')
337
+ end
338
+ else
339
+ ::Libuv::Q.reject(@loop, 'debug failed: system #{sys} lookup failed')
336
340
  end
341
+ }).then(proc { |mod_id|
342
+ do_debug(id, mod_id, sys, mod, index)
343
+ }).catch do |err|
344
+ if err.is_a? String
345
+ @logger.info(err)
346
+ error_response(id, ERRORS[:module_not_found], err)
347
+ else
348
+ @logger.print_error(err, "debug request failed: #{params}")
349
+ error_response(id, ERRORS[:module_not_found], "debug request failed for: #{sys}->#{mod}_#{index}")
350
+ end
351
+ end
352
+ else
353
+ do_debug(id, mod)
354
+ end
355
+ end
337
356
 
338
- @ws.text(::JSON.generate({
339
- id: id,
340
- type: :success
341
- }))
357
+ def do_debug(id, mod, sys_id = nil, mod_name = nil, mod_index = nil)
358
+ resp = {
359
+ id: id,
360
+ type: :success,
361
+ mod_id: mod
362
+ }
363
+
364
+ if mod_name
365
+ # Provide meta information for convenience
366
+ # Actual debug messages do not contain this info
367
+ # The library must use the mod_id returned in the response to route responses as desired
368
+ resp[:meta] = {
369
+ sys: sys_id,
370
+ mod: mod_name,
371
+ index: mod_index
372
+ }
373
+ end
374
+
375
+ # Set mod to get module level errors
376
+ begin
377
+ if @inspecting.include?(mod)
378
+ @ws.text(::JSON.generate(resp))
342
379
  else
343
- @logger.info("websocket debug could not find module: #{mod}")
344
- error_response(id, ERRORS[:module_not_found], "could not find module: #{mod}")
380
+ mod_man = ::Orchestrator::Control.instance.loaded?(mod)
381
+ if mod_man
382
+ log = mod_man.logger
383
+ log.add @debug
384
+ log.level = :debug
385
+ @inspecting.add mod
386
+
387
+ # Set sys to get errors occurring outside of the modules
388
+ if !@inspecting.include?(:self)
389
+ @logger.add @debug
390
+ @logger.level = :debug
391
+ @inspecting.add :self
392
+ end
393
+
394
+ @ws.text(::JSON.generate(resp))
395
+ else
396
+ @logger.info("websocket debug could not find module: #{mod}")
397
+ error_response(id, ERRORS[:module_not_found], "could not find module: #{mod}")
398
+ end
345
399
  end
346
- else
347
- @ws.text(::JSON.generate({
348
- id: id,
349
- type: :success
350
- }))
400
+ rescue => e
401
+ @logger.print_error(e, "websocket debug request failed")
402
+ error_response(id, ERRORS[:request_failed], e.message)
351
403
  end
352
404
  end
353
405
 
@@ -418,8 +470,46 @@ module Orchestrator
418
470
  @bindings = nil
419
471
  @debug.resolve(true) if @debug # detach debug listeners
420
472
 
473
+ @accessTimer.cancel
474
+ @loop.work(proc {
475
+ @accesslock.synchronize {
476
+ @access_log.systems = @accessed.to_a
477
+ @access_log.ended_at = Time.now.to_i
478
+ @access_log.save
479
+ }
480
+ })
481
+ end
482
+
483
+
484
+ protected
485
+
486
+
487
+ def update_accessed(*args)
488
+ if @accesslock.try_lock # No blocking!
489
+ begin
490
+ @access_log.systems = @accessed.to_a
491
+
492
+ @loop.work(proc {
493
+ @access_log.save
494
+ }).finally do
495
+ @accesslock.unlock
496
+ end
497
+ rescue => e
498
+ @accesslock.unlock if @accesslock.locked?
499
+ @logger.print_error(e, "unknown error writing access log")
500
+ end
501
+ end
502
+ end
503
+
504
+ def periodicly_update_logs
505
+ @accessTimer = @loop.scheduler.every(60000 + Random.rand(1000), method(:update_accessed))
506
+ @accesslock = Mutex.new
421
507
  @access_log.systems = @accessed.to_a
422
- @access_log.save
508
+ @loop.work(proc {
509
+ @accesslock.synchronize {
510
+ @access_log.save
511
+ }
512
+ })
423
513
  end
424
514
  end
425
515
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: orchestrator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen von Takach
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-19 00:00:00.000000000 Z
11
+ date: 2015-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -190,8 +190,10 @@ files:
190
190
  - README.md
191
191
  - Rakefile
192
192
  - app/controllers/orchestrator/api/dependencies_controller.rb
193
+ - app/controllers/orchestrator/api/logs_controller.rb
193
194
  - app/controllers/orchestrator/api/modules_controller.rb
194
195
  - app/controllers/orchestrator/api/systems_controller.rb
196
+ - app/controllers/orchestrator/api/users_controller.rb
195
197
  - app/controllers/orchestrator/api/zones_controller.rb
196
198
  - app/controllers/orchestrator/api_controller.rb
197
199
  - app/controllers/orchestrator/base.rb
@@ -199,6 +201,7 @@ files:
199
201
  - app/models/orchestrator/access_log.rb
200
202
  - app/models/orchestrator/control_system.rb
201
203
  - app/models/orchestrator/dependency.rb
204
+ - app/models/orchestrator/edge_control.rb
202
205
  - app/models/orchestrator/mod/by_dependency/map.js
203
206
  - app/models/orchestrator/mod/by_module_type/map.js
204
207
  - app/models/orchestrator/module.rb
@@ -206,6 +209,7 @@ files:
206
209
  - app/models/orchestrator/sys/by_zones/map.js
207
210
  - app/models/orchestrator/zone.rb
208
211
  - app/models/orchestrator/zone/all/map.js
212
+ - app/models/user.rb
209
213
  - config/routes.rb
210
214
  - lib/generators/module/USAGE
211
215
  - lib/generators/module/module_generator.rb
@@ -231,6 +235,9 @@ files:
231
235
  - lib/orchestrator/logger.rb
232
236
  - lib/orchestrator/logic/manager.rb
233
237
  - lib/orchestrator/logic/mixin.rb
238
+ - lib/orchestrator/remote/edge.rb
239
+ - lib/orchestrator/remote/master.rb
240
+ - lib/orchestrator/remote/proxy.rb
234
241
  - lib/orchestrator/service/manager.rb
235
242
  - lib/orchestrator/service/mixin.rb
236
243
  - lib/orchestrator/service/transport_http.rb
@@ -262,7 +269,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
262
269
  version: '0'
263
270
  requirements: []
264
271
  rubyforge_project:
265
- rubygems_version: 2.2.2
272
+ rubygems_version: 2.4.5
266
273
  signing_key:
267
274
  specification_version: 4
268
275
  summary: A distributed system for building automation