rhcp 0.1.2 → 0.1.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.
data/lib/rhcp/command.rb DELETED
@@ -1,120 +0,0 @@
1
- module RHCP
2
-
3
- require 'rubygems'
4
- require 'json'
5
-
6
- #require 'rhcp/rhcp'
7
- require 'rhcp/response'
8
-
9
- # This class represents a single RHCP command, i.e. a functionality that
10
- # should be exposed to clients.
11
- class Command
12
-
13
- # TODO add read_write/read_only marker
14
-
15
- # TODO add metadata about the command's result (display type ("table"), column order etc.)
16
-
17
- # a unique name for this command
18
- attr_reader :name
19
-
20
- # textual description of what this command is doing
21
- attr_reader :description
22
-
23
- # the parameters supported by this command
24
- attr_reader :params
25
-
26
- # the block that should be executed if this command is invoked
27
- attr_reader :block
28
-
29
- # the parameter that is the default param for this command; might be nil
30
- attr_reader :default_param
31
-
32
- def initialize(name, description, block)
33
- @logger = RHCP::ModuleHelper.instance().logger
34
-
35
- @name = name
36
- @description = description
37
- @block = block
38
- @params = Hash.new()
39
- @default_param = nil
40
- end
41
-
42
- # adds a new parameter and returns the command
43
- def add_param(new_param)
44
- raise "duplicate parameter name '#{new_param.name}'" if @params.has_key?(new_param.name)
45
- @params[new_param.name] = new_param
46
- if new_param.is_default_param
47
- if @default_param != nil
48
- 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")
49
- end
50
- @default_param = new_param
51
- end
52
- self
53
- end
54
-
55
- # returns the specified CommandParam
56
- # or throws an RhcpException if the parameter does not exist
57
- def get_param(param_name)
58
- raise RHCP::RhcpException.new("no such parameter : #{param_name}") unless @params.has_key?(param_name)
59
- @params[param_name]
60
- end
61
-
62
- # invokes this command
63
- def execute_request(request)
64
- @logger.debug "gonna execute request >>#{request}<<"
65
- response = RHCP::Response.new()
66
- begin
67
- # TODO redirect the block's output (both Logger and STDOUT/STDERR) and send it back with the response
68
- result = block.call(request, response)
69
- response.set_payload(result)
70
- rescue => ex
71
- response.mark_as_error(ex.to_s, ex.backtrace.join("\n"))
72
- end
73
-
74
- fire_post_exec_event(request, response)
75
-
76
- response
77
- end
78
-
79
- def execute(param_values = {})
80
- req = RHCP::Request.new(self, param_values)
81
- execute_request(req)
82
- end
83
-
84
-
85
- # we do not serialize the block (no sense in this) nor the default param
86
- # when the command is unmarshalled as stub, the default param will be set again
87
- # automatically ba add_param(), and the block will be replaced by the block
88
- # that does the remote invocation
89
- def to_json(*args)
90
- {
91
- 'name' => @name,
92
- 'description' => @description,
93
- 'params' => @params.values
94
- }.to_json(*args)
95
- end
96
-
97
- # TODO think about moving this and the whole command thing to the broker
98
- def Command.register_post_exec_listener(block)
99
- @@listener = [] unless defined?(@@listener)
100
- @@listener << block
101
- end
102
-
103
- def Command.clear_post_exec_listeners()
104
- @@listener = []
105
- end
106
-
107
- def fire_post_exec_event(request, response)
108
- return unless defined? @@listener
109
- @@listener.each do |listener|
110
- begin
111
- listener.call(request, response)
112
- rescue => ex
113
- @logger.warn "a post-exec listener failed with the following message : #{ex}"
114
- end
115
- end
116
- end
117
-
118
- end
119
-
120
- end
@@ -1,93 +0,0 @@
1
- require 'rubygems'
2
- require 'json'
3
-
4
- require 'rhcp/rhcp_exception'
5
-
6
- module RHCP
7
-
8
- # This class represents a single parameter that can be specified for
9
- # a certain command. It is used by the server side to define which
10
- # commands are available for clients.
11
- class CommandParam
12
-
13
- # a unique name for this parameter
14
- attr_reader :name
15
- # a textual description of this parameter's meaning
16
- attr_reader :description
17
- # "true" if the command can not be invoked without this parameter
18
- attr_reader :mandatory
19
- # "true" if there are lookup values available for this parameter
20
- attr_reader :has_lookup_values
21
- # "true" if this parameter might be specified multiple times, resulting
22
- # in a list of values for this parameter
23
- attr_reader :allows_multiple_values
24
- # "true" if this parameter is the default parameter for the enclosing command
25
- attr_reader :is_default_param
26
-
27
- # creates a new command parameter
28
- # options is a hash, possibly holding the following values (all optional)
29
- # :mandatory true if the parameter is necessary for this command
30
- # :allows_multiple_values true if this parameter might be specified multiple times
31
- # :lookup_method a block that returns an array of lookup values valid for this param
32
- # :is_default_param true if this parameter is the default param for the enclosing command
33
- def initialize(name, description, options = Hash.new)
34
- @name = name
35
- @description = description
36
-
37
- @mandatory = options[:mandatory] || false
38
- @lookup_value_block = options[:lookup_method]
39
- @has_lookup_values = @lookup_value_block != nil
40
- @allows_multiple_values = options[:allows_multiple_values] || false
41
- @is_default_param = options[:is_default_param] || false
42
- end
43
-
44
- # returns lookup values for this parameter
45
- # if "partial_value" is specified, only those values are returned that start
46
- # with "partial_value"
47
- def get_lookup_values(partial_value = "")
48
- if @has_lookup_values
49
- @lookup_value_block.call().grep(/^#{partial_value}/)
50
- else
51
- []
52
- end
53
- end
54
-
55
- # throws an exception if +possible_value+ is not a valid value for this parameter
56
- # returns true otherwise
57
- # note that this methods expects +possible_value+ to be an array since all param values
58
- # are specified as arrays
59
- def check_param_is_valid(possible_value)
60
-
61
- # formal check : single-value params against multi-value params
62
- if ((! @allows_multiple_values) && (possible_value.size > 1))
63
- raise RHCP::RhcpException.new("multiple values specified for single-value parameter '#{@name}'")
64
- end
65
-
66
- # check against lookup values
67
- if @has_lookup_values
68
- possible_value.each do |value|
69
- if ! get_lookup_values().include?(value)
70
- raise RHCP::RhcpException.new("invalid value '#{value}' for parameter '#{@name}'")
71
- end
72
- end
73
- end
74
-
75
- true
76
- end
77
-
78
- # we do not serialize the lookup_method (it couldn't be invoked remotely
79
- # anyway)
80
- def to_json(*args)
81
- {
82
- 'name' => @name,
83
- 'description' => @description,
84
- 'allows_multiple_values' => @allows_multiple_values,
85
- 'has_lookup_values' => @has_lookup_values,
86
- 'is_default_param' => @is_default_param,
87
- 'mandatory' => @mandatory
88
- }.to_json(*args)
89
- end
90
-
91
- end
92
-
93
- end
@@ -1,25 +0,0 @@
1
- require 'rhcp/rhcp_exception'
2
- require 'rhcp/broker'
3
-
4
- module RHCP
5
-
6
- class DispatchingBroker < RHCP::Broker
7
- def initialize
8
- @known_commands = Hash.new()
9
- end
10
-
11
- # returns a list of all known commands
12
- def get_command_list()
13
- @known_commands
14
- end
15
-
16
- def add_broker(new_broker)
17
- new_broker.get_command_list.each do |name, command|
18
- raise RHCP::RhcpException.new("duplicate command: '#{name}' has already been defined.") if @known_commands.has_key?(name)
19
- @known_commands[name] = command
20
- end
21
- end
22
-
23
- end
24
-
25
- end
@@ -1,157 +0,0 @@
1
- require 'webrick'
2
- require 'json'
3
- require 'net/http'
4
-
5
- require 'rhcp/broker'
6
- require 'rhcp/request'
7
- require 'rhcp/rhcp_exception'
8
-
9
- include WEBrick
10
-
11
- module RHCP
12
-
13
- # TODO add some logging
14
-
15
- # The +HttpExporter+ connects to the RHCP::Broker and exports all commands
16
- # registered there via http, i.e. it servers requests to the URLs
17
- #
18
- # /rhcp/get_commands
19
- # /rhcp/get_lookup_values
20
- # /rhcp/execute
21
- #
22
- # All data is transferred in JSON format.
23
- class HttpExporter
24
-
25
- DEFAULT_PORT = 42000
26
- DEFAULT_PREFIX = "rhcp"
27
-
28
- # by default, a new HttpExporter will create it's own +HTTPServer+
29
- # on port 42000 - if you want to change this port, you can pass the port
30
- # to use as +port+ option. If you want to use an existing +HTTPServer+
31
- # instance, you can pass it as +server+ option instead.
32
- #
33
- # Also, you can specify the prefix for the rhcp URLs using the +prefix+
34
- # option - otherwise, all
35
- # rhcp actions will be exported at
36
- # /rhcp/get_commands
37
- # /rhcp/get_lookup_values
38
- # /rhcp/execute
39
- # If you change this, please be aware that all clients that want to connect
40
- # to this server need to be configured accordingly.
41
- def initialize(broker, options = Hash.new())
42
-
43
- @logger = RHCP::ModuleHelper::instance.logger
44
- @broker = broker
45
-
46
- # build your own server or use the on passed in as param
47
- port = options.has_key?(:port) ? options[:port] : DEFAULT_PORT
48
-
49
- if options.has_key?(:server)
50
- @server = options[:server]
51
- @logger.debug("using existing server #{@server}")
52
- else
53
- @logger.debug("opening own server on port #{port}")
54
- @server = HTTPServer.new( :Port => port )
55
- end
56
-
57
- @url_prefix = options.has_key?(:prefix) ? options[:prefix] : DEFAULT_PREFIX
58
- @server.mount_proc("/#{@url_prefix}/") {|req, res|
59
- res.body = "<HTML>hello (again)</HTML>"
60
- res['Content-Type'] = "text/html"
61
- }
62
- # TODO this path should probably be quite relative
63
- @server.mount "/#{@url_prefix}/info", HTTPServlet::FileHandler, "docroot"
64
- @server.mount "/#{@url_prefix}/get_commands", GetCommandsServlet, @broker
65
- @server.mount "/#{@url_prefix}/get_lookup_values", GetLookupValuesServlet, @broker
66
- @server.mount "/#{@url_prefix}/execute", ExecuteServlet, @broker
67
- @logger.info("http exporter has been initialized - once started, it will listen on port '#{port}' for URLs starting with prefix '#{@url_prefix}'")
68
- end
69
-
70
- class BaseServlet < HTTPServlet::AbstractServlet
71
-
72
- def initialize(server, broker)
73
- super(server)
74
- @broker = broker
75
- end
76
-
77
- def do_GET(req, res)
78
- res['Content-Type'] = "application/json"
79
- begin
80
- #@logger.info("executing #{req}")
81
- do_it(req, res)
82
- #@logger.info("finished executing #{req}")
83
- rescue RHCP::RhcpException => ex
84
- # TODO define error format (should we send back a JSON-ified response object here?)
85
- @logger.error("got an exception while executing request . #{ex}")
86
- res.status = 500
87
- res.body = [ ex.to_s ].to_json()
88
- #puts ex
89
- #puts ex.backtrace.join("\n")
90
- end
91
- end
92
-
93
- # TODO is this a good idea?
94
- def do_POST(req, res)
95
- do_GET(req, res)
96
- end
97
-
98
- end
99
-
100
- class GetCommandsServlet < BaseServlet
101
-
102
- def do_it(req, res)
103
- commands = @broker.get_command_list
104
- puts "about to send back commands : #{commands.values.to_json()}"
105
- res.body = commands.values.to_json()
106
- end
107
- end
108
-
109
- class GetLookupValuesServlet < BaseServlet
110
-
111
- def do_it(req, res)
112
- partial_value = req.query.has_key?('partial') ? req.query['partial'] : ''
113
- command = @broker.get_command(req.query['command'])
114
- param = command.get_param(req.query['param'])
115
- lookup_values = param.get_lookup_values(partial_value)
116
- res.body = lookup_values.to_json()
117
- end
118
-
119
- end
120
-
121
- class ExecuteServlet < BaseServlet
122
-
123
- def do_it(req, res)
124
- @logger.info("got an execute request : #{req}")
125
- request = RHCP::Request.reconstruct_from_json(@broker, req.body)
126
-
127
- response = request.execute()
128
-
129
- @logger.debug("sending back response : #{response.to_json()}")
130
- res.body = response.to_json()
131
- end
132
-
133
- end
134
-
135
- def run
136
- @server.start
137
- end
138
-
139
- def start
140
- puts "about to start server..."
141
- @thread = Thread.new {
142
- puts "in thread; starting server..."
143
- @server.start
144
- puts "in thread: continuing..."
145
- }
146
- end
147
-
148
- def stop
149
- puts "about to stop server"
150
- @server.stop
151
- puts "thread stopped"
152
- # TODO wait until after the server has ended - when this method exits, the server is still shutting down
153
- end
154
-
155
- end
156
-
157
- end
data/lib/rhcp/request.rb DELETED
@@ -1,93 +0,0 @@
1
- require 'rhcp/rhcp_exception'
2
- require 'rhcp/broker'
3
-
4
- require 'rubygems'
5
- require 'json'
6
-
7
- module RHCP
8
-
9
- # This class represents a request made by a RHCP client to a RHCP server.
10
- # It is passed as an argument to the method that should execute the requested
11
- # command
12
- class Request
13
-
14
- attr_reader :command
15
- attr_reader :param_values
16
-
17
- # default constructor; will throw exceptions on invalid values
18
- def initialize(command, param_values = {})
19
- raise RHCP::RhcpException.new("command may not be null") if command == nil
20
-
21
- # autobox the parameters if necessary
22
- param_values.each do |k,v|
23
- if ! v.instance_of?(Array)
24
- param_values[k] = [ v ]
25
- end
26
- end
27
-
28
- # check all param values for plausibility
29
- param_values.each do |key,value|
30
- command.get_param(key).check_param_is_valid(value)
31
- end
32
-
33
- # check that we've got all mandatory params
34
- command.params.each do |name, param|
35
- raise RHCP::RhcpException.new("missing mandatory parameter '#{name}'") if param.mandatory && ! param_values.has_key?(name)
36
- end
37
-
38
- @command = command
39
- @param_values = param_values
40
- end
41
-
42
- # used to retrieve the value for the specified parameter
43
- # returns either the value or an array of values if the parameter allows
44
- # multiple values
45
- def get_param_value(param_name)
46
- raise "no such parameter : #{param_name}" unless @param_values.has_key?(param_name)
47
- param = @command.get_param(param_name)
48
- if (param.allows_multiple_values)
49
- @param_values[param_name]
50
- else
51
- @param_values[param_name][0]
52
- end
53
- end
54
-
55
- def has_param_value(param_name)
56
- @param_values.has_key?(param_name)
57
- end
58
-
59
- # convenience method that executes the command that actually delegates to the
60
- # command that's inside this request
61
- def execute
62
- @command.execute_request(self)
63
- end
64
-
65
- # reconstructs the request from it's JSON representation
66
- # Since the JSON version of a request does hold the command name instead
67
- # of the full command only, a broker is needed to lookup the command by
68
- # it's name
69
- #
70
- # Params:
71
- # +broker+ is the broker to use for command lookup
72
- # +json_data+ is the JSON data that represents the request
73
- def self.reconstruct_from_json(broker, json_data)
74
- object = JSON.parse(json_data)
75
- command = broker.get_command(object['command_name'])
76
- self.new(command, object['param_values'])
77
- end
78
-
79
- # returns a JSON representation of this request.
80
- def to_json(*args)
81
- {
82
- 'command_name' => @command.name,
83
- 'param_values' => @param_values
84
- }.to_json(*args)
85
- end
86
-
87
- def to_s
88
- "#{@command.name} (#{@param_values})"
89
- end
90
-
91
- end
92
-
93
- end