orchestrator 1.0.2 → 1.0.3

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.
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