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
@@ -45,11 +45,17 @@ module Orchestrator
45
45
 
46
46
 
47
47
  class RequestProxy
48
- def initialize(thread, mod)
48
+ def initialize(thread, mod, user = nil)
49
49
  @mod = mod
50
50
  @thread = thread
51
+ @user = user
52
+ @trace = []
51
53
  end
52
54
 
55
+
56
+ attr_reader :trace
57
+
58
+
53
59
  # Simplify access to status variables as they are thread safe
54
60
  def [](name)
55
61
  @mod.instance[name]
@@ -77,6 +83,11 @@ module Orchestrator
77
83
  end
78
84
  end
79
85
 
86
+ # Looks up the arity of a method
87
+ def arity(method)
88
+ @mod.instance.method(method.to_sym).arity
89
+ end
90
+
80
91
  # All other method calls are wrapped in a promise
81
92
  def method_missing(name, *args, &block)
82
93
  defer = @thread.defer
@@ -90,14 +101,25 @@ module Orchestrator
90
101
  defer.reject(err)
91
102
  @mod.logger.warn(err.message)
92
103
  else
104
+ @trace = caller
105
+
93
106
  @mod.thread.schedule do
107
+ # Keep track of previous in case of recursion
108
+ previous = nil
94
109
  begin
110
+ if @user
111
+ previous = @mod.current_user
112
+ @mod.current_user = @user
113
+ end
114
+
95
115
  defer.resolve(
96
116
  @mod.instance.public_send(name, *args, &block)
97
117
  )
98
118
  rescue => e
99
- @mod.logger.print_error(e)
119
+ @mod.logger.print_error(e, '', @trace)
100
120
  defer.reject(e)
121
+ ensure
122
+ @mod.current_user = previous if @user
101
123
  end
102
124
  end
103
125
  end
@@ -1,22 +1,65 @@
1
+ require 'forwardable'
2
+
1
3
  module Orchestrator
2
4
  module Core
3
5
  class RequestsProxy
4
- def initialize(thread, modules)
6
+ extend Forwardable
7
+
8
+
9
+ def initialize(thread, modules, user = nil)
5
10
  if modules.nil?
6
11
  @modules = []
7
12
  else
8
13
  @modules = modules.is_a?(Array) ? modules : [modules]
9
14
  end
10
15
  @thread = thread
16
+ @user = user
17
+ @trace = []
18
+ end
19
+
20
+
21
+ attr_reader :trace
22
+
23
+
24
+ # Provide Enumerable support
25
+ def each
26
+ return enum_for(:each) unless block_given?
27
+
28
+ @modules.each do |mod|
29
+ yield RequestProxy.new(@thread, mod, @user)
30
+ end
11
31
  end
12
32
 
33
+ # Provide some helper methods
34
+ def_delegators :@modules, :count, :length, :empty?, :each_index
35
+
36
+ def last
37
+ mod = @modules.last
38
+ return nil unless mod
39
+ return RequestProxy.new(@thread, mod, @user)
40
+ end
41
+
42
+ def first
43
+ mod = @modules.first
44
+ return nil unless mod
45
+ return RequestProxy.new(@thread, mod, @user)
46
+ end
47
+
48
+ def [](index)
49
+ mod = @modules[index]
50
+ return nil unless mod
51
+ return RequestProxy.new(@thread, mod, @user)
52
+ end
53
+ alias_method :at, :[]
54
+
13
55
  # Returns true if there is no object to proxy
56
+ # Allows RequestProxy and RequestsProxy to be used interchangably
14
57
  #
15
58
  # @return [true|false]
16
59
  def nil?
17
60
  @modules.empty?
18
61
  end
19
- alias_method :empty?, :nil?
62
+
20
63
 
21
64
  def method_missing(name, *args, &block)
22
65
  if ::Orchestrator::Core::PROTECTED[name]
@@ -24,16 +67,27 @@ module Orchestrator
24
67
  ::Libuv::Q.reject(@thread, err)
25
68
  # TODO:: log warning err.message
26
69
  else
70
+ @trace = caller
71
+
27
72
  promises = @modules.map do |mod|
28
73
  defer = mod.thread.defer
29
74
  mod.thread.schedule do
75
+ # Keep track of previous in case of recursion
76
+ previous = nil
30
77
  begin
78
+ if @user
79
+ previous = mod.current_user
80
+ mod.current_user = @user
81
+ end
82
+
31
83
  defer.resolve(
32
84
  mod.instance.public_send(name, *args, &block)
33
85
  )
34
86
  rescue => e
35
- mod.logger.print_error(e)
87
+ mod.logger.print_error(e, '', @trace)
36
88
  defer.reject(e)
89
+ ensure
90
+ mod.current_user = previous if @user
37
91
  end
38
92
  end
39
93
  defer.promise
@@ -4,10 +4,11 @@ require 'set'
4
4
  module Orchestrator
5
5
  module Core
6
6
  class SystemProxy
7
- def initialize(thread, sys_id, origin = nil)
7
+ def initialize(thread, sys_id, origin = nil, user = nil)
8
8
  @system = sys_id.to_sym
9
9
  @thread = thread
10
10
  @origin = origin # This is the module that requested the proxy
11
+ @user = user
11
12
  end
12
13
 
13
14
  # Alias for get
@@ -24,7 +25,7 @@ module Orchestrator
24
25
  index -= 1 # Get the real index
25
26
  name = mod.to_sym
26
27
 
27
- RequestProxy.new(@thread, system.get(name, index))
28
+ RequestProxy.new(@thread, system.get(name, index), @user)
28
29
  end
29
30
 
30
31
  # Checks for the existence of a particular module
@@ -44,8 +45,7 @@ module Orchestrator
44
45
  # @param module [String, Symbol] the name of the module in the system
45
46
  # @return [::Orchestrator::Core::RequestsProxy] proxies requests to multiple modules
46
47
  def all(mod)
47
- name = mod.to_sym
48
- RequestsProxy.new(@thread, system.all(name))
48
+ RequestsProxy.new(@thread, system.all(mod.to_sym), @user)
49
49
  end
50
50
 
51
51
  # Iterates over the modules in the system. Can also specify module types.
@@ -67,8 +67,7 @@ module Orchestrator
67
67
  # @param module [String, Symbol] the name of the module in the system
68
68
  # @return [Integer] the number of modules with a shared name
69
69
  def count(mod)
70
- name = mod.to_sym
71
- system.count(name)
70
+ system.count(mod.to_sym)
72
71
  end
73
72
 
74
73
  # Returns a list of all the module names in the system
@@ -85,6 +84,13 @@ module Orchestrator
85
84
  system.config.name
86
85
  end
87
86
 
87
+ # Returns the system id as defined in the database
88
+ #
89
+ # @return [Symbol] the id of the system
90
+ def id
91
+ @system
92
+ end
93
+
88
94
  # Used to be notified when an update to a status value occurs
89
95
  #
90
96
  # @param module [String, Symbol] the name of the module in the system
@@ -34,6 +34,7 @@ module Orchestrator
34
34
  # Other options include:
35
35
  # * emit callback to occur once command complete (may be discarded if a named command)
36
36
  # * on_receive (alternative to received function)
37
+ # * clear_queue (clear further commands once this has run)
37
38
  }
38
39
 
39
40
  CONFIG_DEFAULTS = {
@@ -425,6 +426,12 @@ module Orchestrator
425
426
  command[:defer].resolve(:no_wait)
426
427
  call_emit command # the command has been sent
427
428
  end
429
+
430
+ # Useful for emergency stops etc
431
+ if command[:clear_queue]
432
+ @queue.cancel_all("Command #{command[:name]} cleared the queue")
433
+ end
434
+
428
435
  nil # ensure promise chain is not propagated
429
436
  end
430
437
 
@@ -36,6 +36,7 @@ module Orchestrator
36
36
  # Discover the possible module location paths after initialization is complete
37
37
  #
38
38
  config.after_initialize do |app|
39
+ require File.expand_path(File.join(File.expand_path("../", __FILE__), '../../app/models/user'))
39
40
 
40
41
  ActiveSupport::Dependencies.autoload_paths.each do |path|
41
42
  Pathname.new(path).ascend do |v|
@@ -87,9 +87,10 @@ module Orchestrator
87
87
  end
88
88
  end
89
89
 
90
- def print_error(e, msg = '')
90
+ def print_error(e, msg = '', trace = nil)
91
91
  msg << "\n#{e.message}"
92
92
  msg << "\n#{e.backtrace.join("\n")}" if e.respond_to?(:backtrace) && e.backtrace
93
+ msg << "\nCaller backtrace:\n#{trace.join("\n")}" if trace
93
94
  error(msg)
94
95
  end
95
96
 
@@ -10,8 +10,8 @@ module Orchestrator
10
10
  end
11
11
 
12
12
  # Access to other modules in the same control system
13
- def system
14
- @system ||= ::Orchestrator::Core::SystemProxy.new(@thread, @settings.control_system_id)
13
+ def system(user = nil)
14
+ ::Orchestrator::Core::SystemProxy.new(@thread, @settings.control_system_id, nil, user)
15
15
  end
16
16
  end
17
17
  end
@@ -4,7 +4,7 @@ module Orchestrator
4
4
  include ::Orchestrator::Core::Mixin
5
5
 
6
6
  def system
7
- @__config__.system
7
+ @__config__.system(current_user)
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,30 @@
1
+ require 'set'
2
+
3
+
4
+ module Orchestrator
5
+
6
+ class Edge
7
+ def initialize(edge_ip, thread)
8
+ @thread = thread
9
+ @ip = edge_ip
10
+
11
+ reconnect
12
+ end
13
+
14
+
15
+ attr_reader :thread
16
+
17
+
18
+ def exec()
19
+
20
+ end
21
+
22
+
23
+ protected
24
+
25
+
26
+ def reconnect
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,150 @@
1
+ require 'set'
2
+ require 'json'
3
+
4
+
5
+ module Orchestrator
6
+
7
+ class Master
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
+ @requests = {} # req_id => defer
18
+ @req_map = {} # connection => ::Set.new (req_id)
19
+
20
+ @signal_bind = @thread.async method(:bind_actual)
21
+ @signal_unbind = @thread.async method(:unbind_actual)
22
+
23
+ @request_id = 0
24
+ end
25
+
26
+
27
+ attr_reader :thread
28
+
29
+
30
+ # ping
31
+ # pong
32
+ # exec
33
+ # bind
34
+ # unbind
35
+ # notify
36
+ # status
37
+ # success
38
+ # failure
39
+
40
+
41
+ def request(edge_id, details)
42
+ defer = @thread.defer
43
+
44
+ # Lookup node
45
+ connection = online? id
46
+ if connection
47
+ @thread.schedule do
48
+ if connection.connected
49
+ @request_id += 1
50
+ @requests[@request_id] = defer
51
+ @req_map[connection] ||= ::Set.new
52
+ @req_map[connection] << @request_id
53
+
54
+ # Send the request
55
+ connection.write(::JSON.fast_generate({
56
+ id: @request_id,
57
+
58
+ })).catch do |reason|
59
+ on_failure(defer, edge_id, details)
60
+ end
61
+ else
62
+ on_failure(defer, edge_id, details)
63
+ end
64
+ end
65
+ else
66
+ on_failure(defer, edge_id, details)
67
+ end
68
+
69
+ defer.promise
70
+ end
71
+
72
+ def online?(id)
73
+ edge = @edge_nodes[id]
74
+ edge && edge.connected ? edge : false
75
+ end
76
+
77
+ def unbind
78
+ @signal_unbind.call
79
+ end
80
+
81
+ def bind
82
+ @signal_bind.call
83
+ end
84
+
85
+
86
+ protected
87
+
88
+
89
+ def on_failure(defer, edge_id, details)
90
+ # Failed...
91
+ # Are we loading this device locally or remotely?
92
+ # Do we wait a small amount of time before trying again?
93
+ # When should we fail the request?
94
+ end
95
+
96
+
97
+ # These are async methods.. They could be called more than once
98
+ def unbind_actual(*args)
99
+ return if @shutdown
100
+ @shutdown = true
101
+
102
+ @tcp.close unless @tcp.nil?
103
+ @tcp = nil
104
+ end
105
+
106
+ def bind_actual(*args)
107
+ return unless @shutdown
108
+ @shutdown = false
109
+
110
+ # Bind the socket
111
+ @tcp = @thread.tcp
112
+ @tcp.bind '0.0.0.0', 17838, @new_connection
113
+ @tcp.listen 100 # smallish backlog is all we need
114
+
115
+ # Delegate errors
116
+ @tcp.catch @bind_error
117
+ @tcp
118
+ end
119
+
120
+
121
+ # There is a new connection pending. We accept it
122
+ def new_connection(server)
123
+ server.accept @accept_connection
124
+ end
125
+
126
+ # Once the connection is accepted we disable Nagles Algorithm
127
+ # This improves performance as we are using vectored or scatter/gather IO
128
+ # Then the spider delegates to the gazelle loops
129
+ def accept_connection(client)
130
+ client.enable_nodelay
131
+ # TODO:: auth client and then signal the interested parties
132
+ end
133
+
134
+ # Called when binding is closed due to an error
135
+ def bind_error(err)
136
+ return if @shutdown
137
+
138
+ # TODO:: log the error
139
+
140
+ # Attempt to recover!
141
+ @thread.scheduler.in(1000) do
142
+ bind
143
+ end
144
+ end
145
+
146
+ def process_request(defer, node, request)
147
+
148
+ end
149
+ end
150
+ end