rhcp 0.1.9 → 0.2.14

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.
data/lib/rhcp.rb CHANGED
@@ -10,8 +10,10 @@ require 'rhcp/request'
10
10
  require 'rhcp/response'
11
11
  require 'rhcp/rhcp_exception'
12
12
  require 'rhcp/client/http_broker'
13
+ require 'rhcp/client/context_aware_broker'
13
14
  require 'rhcp/client/command_stub'
14
15
  require 'rhcp/client/command_param_stub'
16
+ require 'rhcp/logging_broker'
15
17
 
16
18
  module RHCP #:nodoc:
17
19
 
@@ -20,8 +22,8 @@ module RHCP #:nodoc:
20
22
  include Singleton
21
23
 
22
24
  MAJOR = 0
23
- MINOR = 1
24
- TINY = 9
25
+ MINOR = 2
26
+ TINY = 14
25
27
 
26
28
  def Version.to_s
27
29
  [ MAJOR, MINOR, TINY ].join(".")
@@ -45,3 +47,5 @@ module RHCP #:nodoc:
45
47
  end
46
48
 
47
49
  end
50
+
51
+ $logger = Logger.new(STDOUT)
data/lib/rhcp/broker.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'rhcp/context'
2
+
1
3
  module RHCP
2
4
 
3
5
  # Applications register the commands they are implementing at a broker.
@@ -6,24 +8,17 @@ module RHCP
6
8
  class Broker
7
9
 
8
10
  attr_reader :name
11
+ attr_reader :known_commands
9
12
 
10
13
  def initialize(name = "")
11
14
  # command_name => command
12
15
  @known_commands = Hash.new()
13
16
  @name = name
14
17
  end
15
-
16
- # returns a list of all known commands
17
- def get_command_list()
18
- @known_commands
19
- end
20
-
21
- # returns the specified command object
22
- def get_command(command_name)
23
- raise RHCP::RhcpException.new("no such command : #{command_name}") unless @known_commands.has_key?(command_name)
24
- get_command_list[command_name]
25
- end
26
18
 
19
+ ################################################################################################
20
+ # public server interface
21
+
27
22
  # registers a new command - this method should be called by the application
28
23
  # providing the command
29
24
  def register_command(command)
@@ -31,10 +26,72 @@ module RHCP
31
26
  @known_commands[command.name] = command
32
27
  end
33
28
 
29
+ def unregister_command(name)
30
+ raise RHCP::RhcpException.new("no command found with name '#{name}'") unless @known_commands.has_key?(name)
31
+ @known_commands.delete name
32
+ end
33
+
34
34
  # removes all commands that have been registered previously
35
35
  def clear
36
36
  @known_commands = Hash.new()
37
37
  end
38
38
 
39
+ ################################################################################################
40
+ # public client interface
41
+
42
+ # returns a list of all known commands
43
+ def get_command_list(context = RHCP::Context.new())
44
+ result = {}
45
+ @known_commands.each do |name, command|
46
+ if command.is_enabled?(context)
47
+ result[name] = command
48
+ end
49
+ end
50
+ result
51
+ end
52
+
53
+ def execute(request)
54
+ command = get_command(request.command.name, request.context)
55
+ command.execute_request(request)
56
+ end
57
+
58
+ ################################################################################################
59
+ # the following methods are basically helper methods for going through the
60
+ # data returned by get_command_list()
61
+ #
62
+ # there should be no need to override these in descendant brokers
63
+
64
+ # returns the specified command object
65
+ def get_command(command_name, context=RHCP::Context.new())
66
+ commands = get_command_list(context)
67
+
68
+ raise RHCP::RhcpException.new("no such command : #{command_name}") unless commands.has_key?(command_name)
69
+ commands[command_name]
70
+ end
71
+
72
+ # returns a list of all mandatory parameters for the specified command
73
+ def get_mandatory_params(command_name, context=RHCP::Context.new())
74
+ command = get_command(command_name, context)
75
+ command.get_mandatory_params(context)
76
+ end
77
+
78
+ # fetches lookup values for the parameter specified by command_name/param_name
79
+ # might evaluate the values that have already been collected and the current
80
+ # context
81
+ def get_lookup_values(request, param_name)
82
+ command = get_command(request.command.name, request.context)
83
+ command.get_param(param_name).get_lookup_values(request)
84
+ end
85
+
86
+ # throws an exception if +possible_value+ is not a valid value for this parameter
87
+ # returns true otherwise
88
+ # note that this methods expects +possible_value+ to be an array since all param values
89
+ # are specified as arrays
90
+ def check_param_is_valid(request, param_name, possible_value)
91
+ command = get_command(request.command.name, request.context)
92
+ param = command.get_param(param_name)
93
+ param.check_param_is_valid(request, possible_value)
94
+ end
95
+
39
96
  end
40
97
  end
@@ -31,13 +31,14 @@ module RHCP
31
31
  :allows_multiple_values => object['allows_multiple_values'],
32
32
  :has_lookup_values => object['has_lookup_values'],
33
33
  :is_default_param => object['is_default_param'],
34
- :mandatory => object['mandatory']
34
+ :mandatory => object['mandatory'],
35
+ :autofill_context_key => object['autofill_context_key']
35
36
  }
36
37
  self.new(*args)
37
38
  end
38
39
 
39
- def remote_get_lookup_values(partial_value = "")
40
- @get_lookup_values_block.call(partial_value)
40
+ def remote_get_lookup_values(context = RHCP::Context.new())
41
+ @get_lookup_values_block.call(context)
41
42
  end
42
43
 
43
44
  end
@@ -0,0 +1,78 @@
1
+ module RHCP
2
+
3
+ module Client
4
+
5
+ # a broker decorator that keeps track of the current context
6
+ class ContextAwareBroker
7
+
8
+ attr_accessor :context
9
+
10
+ def initialize(wrapped_broker)
11
+ @wrapped_broker = wrapped_broker
12
+ @context = RHCP::Context.new()
13
+ end
14
+
15
+ def register_command(command)
16
+ @wrapped_broker.register_command command
17
+ end
18
+
19
+ def clear
20
+ @wrapped_broker.clear()
21
+ end
22
+
23
+ def get_command_list(context=@context)
24
+ @wrapped_broker.get_command_list(context)
25
+ end
26
+
27
+ def give_the_request_some_context_rico(request)
28
+ # TODO think about the errors of your ways, son...
29
+ @context.cookies.each do |k,v|
30
+ request.context.cookies[k] = v
31
+ end
32
+ new_request = RHCP::Request.new(request.command, request.param_values, request.context)
33
+ #$logger.debug("+++ wrapped own request : #{new_request}")
34
+ new_request
35
+ end
36
+
37
+ def execute(request)
38
+ #$logger.debug("+++ ContextAwareBroker.execute (#{request.command.name}, #{request.param_values}, #{request.context}) +++")
39
+
40
+ new_request = give_the_request_some_context_rico(request)
41
+ response = @wrapped_broker.execute(new_request)
42
+
43
+ # store context received with response
44
+ if response.context != nil
45
+ # TODO it would be nice if we could delete cookies as well
46
+ response.context.each do |key,value|
47
+ @context.cookies[key] = value
48
+ $logger.debug "storing value '#{value}' for key '#{key}' in context"
49
+ end
50
+ end
51
+
52
+ response
53
+ end
54
+
55
+ def get_command(command_name, context=@context)
56
+ @wrapped_broker.get_command(command_name, context)
57
+ end
58
+
59
+ def get_mandatory_params(command_name, context=@context)
60
+ command = get_command(command_name)
61
+ command.get_mandatory_params(context)
62
+ end
63
+
64
+ def get_lookup_values(request, param_name)
65
+ request_with_context = give_the_request_some_context_rico(request)
66
+ @wrapped_broker.get_lookup_values(request_with_context, param_name)
67
+ end
68
+
69
+ def check_param_is_valid(request, param_name, possible_value)
70
+ request_with_context = give_the_request_some_context_rico(request)
71
+ @wrapped_broker.check_param_is_valid(request_with_context, param_name, possible_value)
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -11,7 +11,7 @@ module RHCP
11
11
 
12
12
  # This is an implementation of a RHCP broker that retrieves it's data via
13
13
  # http from a remote broker. Since it implements the same interface as RHCP::Broker,
14
- # clients can use it exactly as if they were talking with the broker itself.
14
+ # clients can use it exactly as if they were talking to the broker itself.
15
15
  class HttpBroker < Broker
16
16
 
17
17
  def initialize(url)
@@ -19,7 +19,26 @@ module RHCP
19
19
  @url = url
20
20
  @logger = RHCP::ModuleHelper.instance.logger
21
21
  @logger.debug "connecting to #{@url}"
22
- res = Net::HTTP.new(@url.host, @url.port).start { |http| http.get("/rhcp/") }
22
+
23
+ # req = Net::HTTP::Get.new("/rhcp")
24
+ # if params.has_key?("user") && params.has_key?("password") then
25
+ # req.basic_auth params["user"], params["password"]
26
+ # elsif uri.user != nil then
27
+ # req.basic_auth uri.user, uri.password
28
+ # end
29
+ #
30
+ # http = Net::HTTP.new(uri.host, uri.port)
31
+ # #http.set_debug_output($stderr)
32
+ # @op.log_to_jabber_detail("message" => "HTTP_START #{uri} : #{req}")
33
+ # response = http.request(req)
34
+
35
+ res = Net::HTTP.new(@url.host, @url.port).start { |http|
36
+ if @url.user != nil then
37
+ @logger.debug "using basic auth data: #{@url.user} / #{@url.password}"
38
+ http.basic_auth @url.user, @url.password
39
+ end
40
+ http.get("/rhcp/")
41
+ }
23
42
  if (res.code == "200")
24
43
  @logger.info "connected to '#{@url}' successfully."
25
44
  else
@@ -29,9 +48,9 @@ module RHCP
29
48
  end
30
49
 
31
50
  # returns a list of all known commands
32
- # TODO add caching for commands
33
- def get_command_list()
34
- res = Net::HTTP.new(@url.host, @url.port).start { |http| http.get("/rhcp/get_commands") }
51
+ def get_command_list(context = RHCP::Context.new())
52
+ # TODO CONTEXT include context
53
+ res = Net::HTTP.new(@url.host, @url.port).start { |http| http.post("/rhcp/get_commands", context.to_json) }
35
54
  if (res.code != "200")
36
55
  raise RHCP::RhcpException.new("could not retrieve command list from remote server : http status #{res.code} (#{res.message})")
37
56
  end
@@ -60,13 +79,14 @@ module RHCP
60
79
  }
61
80
 
62
81
  # inject appropriate blocks for all params
63
- command.params.each do |name, param|
82
+ command.params.each do |param|
64
83
  param.get_lookup_values_block = lambda {
65
- |partial_value|
66
- @logger.debug("getting lookup values for param #{name} of command #{command.name} using #{@url}")
84
+ |request|
85
+ @logger.debug("getting lookup values for param #{param.name} of command #{command.name} using #{@url}")
67
86
  # TODO add caching for lookup values
87
+ $logger.debug("request : #{request}")
68
88
  res = Net::HTTP.new(@url.host, @url.port).start { |http|
69
- http.get("/rhcp/get_lookup_values?command=#{command.name}&param=#{name}")
89
+ http.post("/rhcp/get_lookup_values?command=#{command.name}&param=#{param.name}", request.to_json)
70
90
  }
71
91
  if (res.code != "200")
72
92
  raise RHCP::RhcpException.new("could not retrieve lookup values from remote server : http status #{res.code} (#{res.message})")
data/lib/rhcp/command.rb CHANGED
@@ -3,7 +3,6 @@ module RHCP
3
3
  require 'rubygems'
4
4
  require 'json'
5
5
 
6
- #require 'rhcp/rhcp'
7
6
  require 'rhcp/response'
8
7
 
9
8
  # This class represents a single RHCP command, i.e. a functionality that
@@ -16,32 +15,41 @@ module RHCP
16
15
  attr_reader :name
17
16
 
18
17
  # textual description of what this command is doing
19
- attr_reader :description
20
-
18
+ attr_accessor :description
19
+
21
20
  # the parameters supported by this command
22
21
  attr_reader :params
23
22
 
24
23
  # the block that should be executed if this command is invoked
25
- attr_reader :block
24
+ attr_accessor :block
26
25
 
27
26
  # the parameter that is the default param for this command; might be nil
28
27
  attr_reader :default_param
29
28
 
30
29
  # "true" if this command returns data, but does not modify anything.
31
30
  attr_reader :is_read_only
31
+
32
+ # an array of context keys that enable this command
33
+ #
34
+ # if at least one of the listed keys is found in the context, the command
35
+ # is enabled, otherwise not. if no keys are listed, the command is enabled
36
+ # by default
37
+ attr_accessor :enabled_through_context_keys
32
38
 
33
39
  # hints about how the result of this command might be displayed
34
40
  attr_accessor :result_hints
35
41
 
36
- def initialize(name, description, block)
42
+ def initialize(name, description, block = nil)
37
43
  @logger = RHCP::ModuleHelper.instance().logger
38
44
 
39
45
  @name = name
40
46
  @description = description
41
- @block = block
42
- @params = Hash.new()
47
+ @block = block unless block == nil
48
+ @params = Array.new()
49
+
43
50
  @default_param = nil
44
51
  @is_read_only = false
52
+ @enabled_through_context_keys = nil
45
53
  # TODO formalize this! (we need rules how clients should react, otherwise this will be a mess)
46
54
  # TODO in some cases, the display_type could also be guessed from the response, I guess
47
55
  @result_hints = {
@@ -51,10 +59,30 @@ module RHCP
51
59
  }
52
60
  end
53
61
 
62
+ def is_enabled?(context = RHCP::Context.new())
63
+ result = true
64
+
65
+ if @enabled_through_context_keys != nil
66
+ result = false
67
+ @enabled_through_context_keys.each do |key|
68
+ if context.cookies.keys.select { |element| element == key }.size() > 0
69
+ @logger.debug "enabling command #{@name} because of context key #{key}"
70
+ result = true
71
+ break
72
+ end
73
+ end
74
+ end
75
+
76
+ result
77
+ end
78
+
54
79
  # adds a new parameter and returns the command
55
80
  def add_param(new_param)
56
- raise "duplicate parameter name '#{new_param.name}'" if @params.has_key?(new_param.name)
57
- @params[new_param.name] = new_param
81
+ existing_params = @params.select do |param|
82
+ param.name == new_param.name
83
+ end
84
+ raise "duplicate parameter name '#{new_param.name}'" if existing_params.size > 0
85
+ @params << new_param
58
86
  if new_param.is_default_param
59
87
  if @default_param != nil
60
88
  raise RHCP::RhcpException.new("there can be only one default parameter per command, and the parameter '#{@default_param.name}' has already been marked as default")
@@ -76,32 +104,108 @@ module RHCP
76
104
  # returns the specified CommandParam
77
105
  # or throws an RhcpException if the parameter does not exist
78
106
  def get_param(param_name)
79
- raise RHCP::RhcpException.new("no such parameter : #{param_name}") unless @params.has_key?(param_name)
80
- @params[param_name]
107
+ existing_params = @params.select do |param|
108
+ param.name == param_name
109
+ end
110
+
111
+ # there shouldn't be none
112
+ raise RHCP::RhcpException.new("no such parameter : #{param_name}") unless existing_params.size > 0
113
+
114
+ # there can be only one
115
+ raise RHCP::RhcpException.new("BUG: duplicate parameters found") if existing_params.size > 1
116
+
117
+ existing_params.first
118
+ end
119
+
120
+ # returns an array of all mandatory params for this command
121
+ # mandatory parameters that are auto-filled through the context are
122
+ # filtered out
123
+ def get_mandatory_params(context = RHCP::Context.new())
124
+ @params.select do |param|
125
+ param.mandatory &&
126
+ ! param.find_value_in_context(context)
127
+ end
81
128
  end
82
129
 
83
130
  # invokes this command
84
131
  def execute_request(request)
85
132
  @logger.debug "gonna execute request >>#{request}<<"
86
- response = RHCP::Response.new()
133
+
134
+ response = RHCP::Response.new()
135
+ # check all param values for plausibility
136
+ request.param_values.each do |key,value|
137
+ get_param(key).check_param_is_valid(request, value)
138
+ end
139
+
140
+ # check that we've got all mandatory params
141
+ @params.each do |param|
142
+ if param.mandatory && ! request.param_values.has_key?(param.name)
143
+ raise RHCP::RhcpException.new("missing mandatory parameter '#{param.name}' for command '#{self.name}'")
144
+ end
145
+ end
146
+
87
147
  begin
88
148
  # TODO redirect the block's output (both Logger and STDOUT/STDERR) and send it back with the response
89
149
  result = block.call(request, response)
90
150
  response.set_payload(result)
91
- rescue => ex
151
+ rescue Exception => ex
92
152
  response.mark_as_error(ex.to_s, ex.backtrace.join("\n"))
93
153
  end
154
+
155
+ # the command has been executed, so we should clear the store of collected param values
156
+ #request.context.collected_values = {}
94
157
 
95
158
  fire_post_exec_event(request, response)
96
159
 
97
160
  response
98
161
  end
99
162
 
100
- def execute(param_values = {})
101
- req = RHCP::Request.new(self, param_values)
163
+ def execute(param_values = {}, context = RHCP::Context.new())
164
+ req = RHCP::Request.new(self, param_values, context)
102
165
  execute_request(req)
103
166
  end
104
167
 
168
+ def add_as_instance_method(the_instance, additional_param_values = {})
169
+ rhcp_command = self
170
+ command_name = rhcp_command.name
171
+
172
+ already_defined = the_instance.methods.select { |m| m == command_name }.size() > 0
173
+ if already_defined
174
+ the_instance.class.send(:remove_method, command_name.to_sym)
175
+ end
176
+ the_instance.class.send(:define_method, command_name.to_sym) do |*args|
177
+ # args is an array - but we need the (optional) hash on index 0
178
+ param_values = {}
179
+ if args.length > 0
180
+ $logger.debug("args: #{args}")
181
+ param_values = args.first
182
+ end
183
+
184
+ # we need to carry along the context
185
+ the_broker = Thread.current['broker']
186
+
187
+ param_values.merge! additional_param_values
188
+
189
+ #param_values["host"] = @hostname if self.class.to_s == "HostController"
190
+
191
+ request = RHCP::Request.new(rhcp_command, param_values, the_broker.context)
192
+
193
+ response = the_broker.execute(request)
194
+
195
+ if (response.status != RHCP::Response::Status::OK) then
196
+ # TODO actually, it would be nice if we could log the details into a file and show only the message
197
+ filtered_error_detail = response.error_detail.split("\n").select do |line|
198
+ /lib\/plugins\//.match(line)
199
+ end.join("\n")
200
+
201
+ $logger.error("#{response.error_text}\n#{response.error_detail}")
202
+ #$logger.error("#{response.error_text}")
203
+ raise RuntimeError.new(response.error_text)
204
+ end
205
+ response.data
206
+ end
207
+ end
208
+
105
209
 
106
210
  # we do not serialize the block (no sense in this) nor the default param
107
211
  # when the command is unmarshalled as stub, the default param will be set again
@@ -111,9 +215,10 @@ module RHCP
111
215
  {
112
216
  'name' => @name,
113
217
  'description' => @description,
114
- 'params' => @params.values,
218
+ 'params' => @params,
115
219
  'read_only' => @is_read_only,
116
- 'result_hints' => @result_hints
220
+ 'result_hints' => @result_hints,
221
+ 'enabled_through_context_keys' => @enabled_through_context_keys
117
222
  }.to_json(*args)
118
223
  end
119
224